Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/
- [`webpack@5.98.0`](https://npmjs.com/package/webpack/)
- Fixed [#5446](https://github.com/microsoft/BotFramework-WebChat/issues/5446). Embedded `uuid` so `microsoft-cognitiveservices-speech-sdk` do not need to use dynamic loading, as this could fail in Webpack 4 environment, in PR [#5445](https://github.com/microsoft/BotFramework-WebChat/pull/5445), by [@compulim](https://github.com/compulim)
- Fixed [#5476](https://github.com/microsoft/BotFramework-WebChat/issues/5476). Modernizing components through memoization and use [`valibot`](https://npmjs.com/package/valibot) for props validation, by [@compulim](https://github.com/compulim)
- Ported `useSuggestedActions` to use React hooks as backend instead of Redux store, in PR [#5489](https://github.com/microsoft/BotFramework-WebChat/pull/5489), by [@compulim](https://github.com/compulim)

### Fixed

Expand Down
25 changes: 25 additions & 0 deletions __tests__/html2/simple.emulator.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<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>
run(async function () {
const {
testHelpers: { createDirectLineEmulator }
} = window;

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();
});
</script>
</body>
</html>
76 changes: 76 additions & 0 deletions __tests__/html2/store/clearSuggestedActions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<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="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';

run(async function () {
const {
testHelpers: { createDirectLineEmulator },
WebChat: { testIds }
} = window;

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: Receive an activity with a suggested action.
await directLine.emulateIncomingActivity({
from: { role: 'bot' },
suggestedActions: {
actions: [{ title: 'Aloha!', type: 'imBack' }],
to: ''
},
text: 'Hello, World!',
type: 'message'
});

await pageConditions.numActivitiesShown(1);

// THEN: Should have one suggested action button shown.
expect(pageElements.allByTestId(testIds.suggestedActionButton)).toHaveLength(1);
expect(pageElements.allByTestId(testIds.suggestedActionButton)[0]).toHaveProperty('textContent', 'Aloha!');

// THEN: getState() should have 1 suggested actions and origin activity.
expect(store.getState().suggestedActions).toHaveLength(1);
expect(store.getState().suggestedActions[0]).toEqual({ title: 'Aloha!', type: 'imBack' });
expect(store.getState().suggestedActionsOriginActivity).toEqual({
activity: expect.objectContaining({
from: { role: 'bot' },
suggestedActions: {
actions: [{ title: 'Aloha!', type: 'imBack' }],
to: ''
},
text: 'Hello, World!',
type: 'message'
})
});

// WHEN: Dispatching "WEB_CHAT/CLEAR_SUGGESTED_ACTIONS" action.
store.dispatch({ type: 'WEB_CHAT/CLEAR_SUGGESTED_ACTIONS' });

// THEN: Should not remove activity.
expect(pageElements.activities()).toHaveLength(1);

// THEN: Should have cleared suggested action.
await waitFor(() => expect(pageElements.allByTestId(testIds.suggestedActionButton)).toHaveLength(0));
});
</script>
</body>
</html>
62 changes: 62 additions & 0 deletions __tests__/html2/store/setSuggestedActions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<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="importmap">
{
"imports": {
"@testduet/wait-for": "https://esm.sh/@testduet/wait-for"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';

run(async function () {
const {
testHelpers: { createDirectLineEmulator },
WebChat: { testIds }
} = window;

const { directLine, store } = createDirectLineEmulator();

WebChat.renderWebChat({ directLine, store }, document.getElementById('webchat'));

await pageConditions.uiConnected();

// WHEN: Dispatching "WEB_CHAT/SET_SUGGESTED_ACTIONS" action.
store.dispatch({
payload: {
suggestedActions: [
{ title: 'Hello, World!', type: 'imBack' },
{ title: 'Aloha!', type: 'imBack' }
]
},
type: 'WEB_CHAT/SET_SUGGESTED_ACTIONS'
});

// THEN: Should not show activity.
expect(pageElements.activities()).toHaveLength(0);

// THEN: Should show 2 suggested actions.
await waitFor(() => expect(pageElements.allByTestId(testIds.suggestedActionButton)).toHaveLength(2));
expect(pageElements.allByTestId(testIds.suggestedActionButton)[0]).toHaveProperty(
'textContent',
'Hello, World!'
);
expect(pageElements.allByTestId(testIds.suggestedActionButton)[1]).toHaveProperty('textContent', 'Aloha!');

// THEN: getState() should have 2 suggested actions.
expect(store.getState().suggestedActions).toHaveLength(2);
expect(store.getState().suggestedActions[0]).toEqual({ title: 'Hello, World!', type: 'imBack' });
expect(store.getState().suggestedActions[1]).toEqual({ title: 'Aloha!', type: 'imBack' });
});
</script>
</body>
</html>
146 changes: 146 additions & 0 deletions __tests__/html2/store/useSuggestedActions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
<script crossorigin="anonymous" src="https://unpkg.com/react@16.8.6/umd/react.development.js"></script>
<script crossorigin="anonymous" src="https://unpkg.com/react-dom@16.8.6/umd/react-dom.development.js"></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="importmap">
{
"imports": {
"@testduet/wait-for": "https://unpkg.com/@testduet/wait-for@main/dist/wait-for.mjs",
"react-dictate-button/internal": "https://unpkg.com/react-dictate-button@main/dist/react-dictate-button.internal.mjs"
}
}
</script>
<script type="module">
import { waitFor } from '@testduet/wait-for';
import renderHook from '../hooks/private/renderHook.js';

const {
React: { createElement },
testHelpers: { createDirectLineEmulator },
WebChat: {
Components: { BasicWebChat, Composer },
hooks: { useSuggestedActions },
testIds
}
} = window;

run(async function () {
const { directLine, store } = createDirectLineEmulator();
const WebChatWrapper = ({ children }) =>
createElement(Composer, { directLine, store }, createElement(BasicWebChat), children);

// WHEN: Render initially.
const renderResult = renderHook(
props => {
const state = useSuggestedActions();

if (props) {
state[1](props.suggestedActions);
} else {
return state;
}
},
{
legacyRoot: true,
wrapper: WebChatWrapper
}
);

await pageConditions.uiConnected();

// THEN: `useSuggestedActions` should return empty array.
await waitFor(() =>
expect(renderResult).toHaveProperty('result.current', [[], expect.anything(), { activity: undefined }])
);

// WHEN: An activity with 2 suggested actions is received.
await directLine.emulateIncomingActivity({
from: { role: 'bot' },
suggestedActions: {
actions: [
{ title: 'Hello, World!', type: 'imBack' },
{ title: 'Aloha!', type: 'imBack' }
],
to: ''
},
text: 'Hello, World!',
type: 'message'
});

// THEN: useSuggestedActions() should return 2 suggested actions.
renderResult.rerender();

await waitFor(() =>
expect(renderResult).toHaveProperty('result.current', [
[
{ title: 'Hello, World!', type: 'imBack' },
{ title: 'Aloha!', type: 'imBack' }
],
expect.anything(),
{
activity: expect.objectContaining({
from: { role: 'bot' },
suggestedActions: {
actions: [
{ title: 'Hello, World!', type: 'imBack' },
{ title: 'Aloha!', type: 'imBack' }
],
to: ''
},
text: 'Hello, World!',
type: 'message'
})
}
])
);

// WHEN: useSuggestedActions() is called with 1 suggested action.
renderResult.rerender({ suggestedActions: [{ title: 'Good morning!', type: 'imBack' }] });

// THEN: Should show 1 suggested action.
await waitFor(() => expect(pageElements.allByTestId(testIds.suggestedActionButton)).toHaveLength(1));
expect(pageElements.allByTestId(testIds.suggestedActionButton)[0]).toHaveProperty(
'textContent',
'Good morning!'
);

// THEN: Should return 1 suggested action.
renderResult.rerender();
await waitFor(() =>
expect(renderResult).toHaveProperty('result.current', [
[{ title: 'Good morning!', type: 'imBack' }],
expect.anything(),
expect.anything()
])
);

// THEN: getState() should have 1 suggested action.
expect(store.getState().suggestedActions).toHaveLength(1);
expect(store.getState().suggestedActions[0]).toEqual({ title: 'Good morning!', type: 'imBack' });

// WHEN: useSuggestedActions() is called with no suggested actions.
renderResult.rerender({ suggestedActions: [] });

// THEN: Should hide suggested actions.
await waitFor(() => expect(pageElements.allByTestId(testIds.suggestedActionButton)).toHaveLength(0));

// THEN: Should return 0 suggested actions.
renderResult.rerender();
await waitFor(() =>
expect(renderResult).toHaveProperty('result.current', [[], expect.anything(), expect.anything()])
);

// THEN: getState() should have 1 suggested action.
expect(store.getState().suggestedActions).toHaveLength(0);
});
</script>
</body>
</html>
Loading
Loading