diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fe3e29722..273d225ba0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `component`: Remove [`react`](https://www.npmjs.com/package/react) and [`react-dom`](https://www.npmjs.com/package/react-dom) from `devDependencies`
- `playground`: Remove [`react`](https://www.npmjs.com/package/react) and [`react-dom`](https://www.npmjs.com/package/react-dom) from `dependencies`
- `samples/*`: Move to production version of Web Chat, and bump to [`react@16.8.6`](https://www.npmjs.com/package/react) and [`react-dom@16.8.6`](https://www.npmjs.com/package/react-dom)
+- Moved the typing indicator to the send box and removed the typing indicator logic from the sagas, by [@tdurnford](https://github.com/tdurnford), in PR [#2321](https://github.com/microsoft/BotFramework-WebChat/pull/2321)
### Fixed
diff --git a/__tests__/__image_snapshots__/chrome-docker/send-typing-indicator-js-typing-indicator-should-display-in-send-box-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/send-typing-indicator-js-typing-indicator-should-display-in-send-box-1-snap.png
new file mode 100644
index 0000000000..bf0ec1f2fc
Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/send-typing-indicator-js-typing-indicator-should-display-in-send-box-1-snap.png differ
diff --git a/__tests__/__image_snapshots__/chrome-docker/send-typing-indicator-js-typing-indicator-should-not-display-after-second-activity-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/send-typing-indicator-js-typing-indicator-should-not-display-after-second-activity-1-snap.png
new file mode 100644
index 0000000000..a2ac36b798
Binary files /dev/null and b/__tests__/__image_snapshots__/chrome-docker/send-typing-indicator-js-typing-indicator-should-not-display-after-second-activity-1-snap.png differ
diff --git a/__tests__/sendTypingIndicator.js b/__tests__/sendTypingIndicator.js
index f289a1166b..8d4567a2ba 100644
--- a/__tests__/sendTypingIndicator.js
+++ b/__tests__/sendTypingIndicator.js
@@ -1,7 +1,10 @@
import { By } from 'selenium-webdriver';
-import { timeouts } from './constants.json';
+import { imageSnapshotOptions, timeouts } from './constants.json';
import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown';
+import typingActivityReceived from './setup/conditions/typingActivityReceived';
+import typingAnimationBackgroundImage from './setup/assets/typingIndicator';
+import uiConnected from './setup/conditions/uiConnected';
// selenium-webdriver API doc:
// https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_WebDriver.html
@@ -20,21 +23,52 @@ test('Send typing indicator', async () => {
await input.sendKeys('ABC');
// Typing indicator takes longer to come back
- await driver.wait(minNumActivitiesShown(3), 5000);
+ await driver.wait(typingActivityReceived(), timeouts.directLine);
});
// TODO: [P3] Take this deprecation code out when releasing on or after January 13 2020
test('Send typing indicator using deprecated props', async () => {
- const { driver, pageObjects } = await setupWebDriver({ props: { sendTyping: true } });
+ const { driver, pageObjects } = await setupWebDriver({
+ props: { sendTyping: true }
+ });
- await pageObjects.sendMessageViaSendBox('echo-typing', { waitForSend: true });
+ await driver.wait(uiConnected(), timeouts.directLine);
- await driver.wait(minNumActivitiesShown(2), timeouts.directLine);
+ await pageObjects.sendMessageViaSendBox('echo-typing', { waitForSend: true });
const input = await driver.findElement(By.css('input[type="text"]'));
await input.sendKeys('ABC');
// Typing indicator takes longer to come back
- await driver.wait(minNumActivitiesShown(3), 5000);
+ await driver.wait(typingActivityReceived(), timeouts.directLine);
+});
+
+test('typing indicator should display in SendBox', async () => {
+ const { driver, pageObjects } = await setupWebDriver({ props: { styleOptions: { typingAnimationBackgroundImage } } });
+
+ await driver.wait(uiConnected(), timeouts.directLine);
+
+ await pageObjects.sendMessageViaSendBox('typing 1', { waitForSend: true });
+
+ // Typing indicator takes longer to come back
+ await driver.wait(typingActivityReceived(), timeouts.directLine);
+
+ const base64PNG = await driver.takeScreenshot();
+
+ expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions);
+});
+
+test('typing indicator should not display after second activity', async () => {
+ const { driver, pageObjects } = await setupWebDriver({
+ props: {
+ styleOptions: { typingAnimationBackgroundImage, typingAnimationDuration: 10000 }
+ }
+ });
+
+ await pageObjects.sendMessageViaSendBox('typing', { waitForSend: true });
+ await driver.wait(minNumActivitiesShown(2), 5000);
+
+ const base64PNG = await driver.takeScreenshot();
+ expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions);
});
diff --git a/__tests__/setup/assets/typingIndicator.js b/__tests__/setup/assets/typingIndicator.js
new file mode 100644
index 0000000000..0a2c39ed64
--- /dev/null
+++ b/__tests__/setup/assets/typingIndicator.js
@@ -0,0 +1 @@
+export default "url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAUACgDASIAAhEBAxEB/8QAGgABAQACAwAAAAAAAAAAAAAAAAYCBwMFCP/EACsQAAECBQIEBQUAAAAAAAAAAAECAwAEBQYRBxITIjFBMlFhccFScoGh8f/EABQBAQAAAAAAAAAAAAAAAAAAAAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwD0lctx023JVD9UeKOIcNoSNylkdcCMbauSmXHLOPUx8r4ZAcQtO1SM9Mj5iO1gtWo1syc7S2zMKYSptbIPNgnII8/5HBpRZ9RpaKjNVVCpUzLPAQ1nmA7qPl6fmAondRrcaqhkVTiiQrYXgglsH7vnpHc3DcNNoEimaqT4Q2s4bCRuUs+gEaLd05uNFVMmiS3o3YEwFDhlP1Z7e3WLzUuzahUKHRk0zM07TmeApvOFLGEjcM9+Xp6wFnbN0Uu5GnF0x4qW1je2tO1Sc9Djy9oRD6QWlU6PPzVSqjRlgtksttKPMcqBKiO3h/cIDacIQgEIQgEIQgP/2Q==')";
diff --git a/__tests__/setup/conditions/typingActivityReceived.js b/__tests__/setup/conditions/typingActivityReceived.js
new file mode 100644
index 0000000000..3536dbeaf5
--- /dev/null
+++ b/__tests__/setup/conditions/typingActivityReceived.js
@@ -0,0 +1,23 @@
+import { Condition } from 'selenium-webdriver';
+
+export default function typingActivityReceived() {
+ return new Condition(
+ `Waiting for typing activity`,
+ async driver =>
+ await driver.executeScript(
+ () =>
+ ~window.WebChatTest.actions
+ .filter(({ type }) => type === 'DIRECT_LINE/INCOMING_ACTIVITY')
+ .findIndex(
+ ({
+ payload: {
+ activity: {
+ from: { role },
+ type
+ }
+ }
+ }) => role === 'bot' && type === 'typing'
+ )
+ )
+ );
+}
diff --git a/packages/bundle/src/index-es5.ts b/packages/bundle/src/index-es5.ts
index 1d61212669..f09455ab50 100644
--- a/packages/bundle/src/index-es5.ts
+++ b/packages/bundle/src/index-es5.ts
@@ -14,6 +14,7 @@ import 'core-js/modules/es.array.iterator';
import 'core-js/modules/es.math.sign';
import 'core-js/modules/es.number.is-finite';
import 'core-js/modules/es.object.assign';
+import 'core-js/modules/es.object.values';
import 'core-js/modules/es.promise';
import 'core-js/modules/es.promise.finally';
import 'core-js/modules/es.string.starts-with';
diff --git a/packages/component/src/Activity/StackedLayout.js b/packages/component/src/Activity/StackedLayout.js
index bff89c8fb4..6e5775fc0d 100644
--- a/packages/component/src/Activity/StackedLayout.js
+++ b/packages/component/src/Activity/StackedLayout.js
@@ -88,8 +88,7 @@ const StackedLayout = ({ activity, avatarInitials, children, language, styleSet,
channelData: { messageBack: { displayText: messageBackDisplayText } = {}, state } = {},
from: { role } = {},
text,
- textFormat,
- type
+ textFormat
} = activity;
const activityDisplayText = messageBackDisplayText || text;
@@ -122,30 +121,20 @@ const StackedLayout = ({ activity, avatarInitials, children, language, styleSet,
)}