From c93080485bf74f7537f22073a7d671c0ea4f60f5 Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 03:13:17 -0800 Subject: [PATCH 01/10] Replace DownloadAttachment and UploadAttachment with FileAttachment --- .../src/Attachment/DownloadAttachment.js | 58 ---------- .../src/Attachment/FileAttachment.js | 38 +++++++ .../component/src/Attachment/FileContent.js | 106 ++++++++++++++++++ .../src/Attachment/ImageAttachment.js | 17 ++- .../src/Attachment/UploadAttachment.js | 56 --------- packages/component/src/Localization/en-US.js | 2 +- .../Attachment/createCoreMiddleware.js | 10 +- .../src/Styles/StyleSet/DownloadAttachment.js | 32 ------ .../src/Styles/StyleSet/FileAttachment.js | 3 + .../src/Styles/StyleSet/FileContent.js | 32 ++++++ .../src/Styles/StyleSet/UploadAttachment.js | 12 -- .../component/src/Styles/createStyleSet.js | 8 +- packages/component/src/index.tsx | 2 + packages/core/src/reducers/activities.js | 23 +++- 14 files changed, 225 insertions(+), 174 deletions(-) delete mode 100644 packages/component/src/Attachment/DownloadAttachment.js create mode 100644 packages/component/src/Attachment/FileAttachment.js create mode 100644 packages/component/src/Attachment/FileContent.js delete mode 100644 packages/component/src/Attachment/UploadAttachment.js delete mode 100644 packages/component/src/Styles/StyleSet/DownloadAttachment.js create mode 100644 packages/component/src/Styles/StyleSet/FileAttachment.js create mode 100644 packages/component/src/Styles/StyleSet/FileContent.js delete mode 100644 packages/component/src/Styles/StyleSet/UploadAttachment.js diff --git a/packages/component/src/Attachment/DownloadAttachment.js b/packages/component/src/Attachment/DownloadAttachment.js deleted file mode 100644 index 737f8a9547..0000000000 --- a/packages/component/src/Attachment/DownloadAttachment.js +++ /dev/null @@ -1,58 +0,0 @@ -import { format } from 'bytes'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import DownloadIcon from './Assets/DownloadIcon'; -import ScreenReaderText from '../ScreenReaderText'; -import useLocalize from '../hooks/useLocalize'; -import useStyleSet from '../hooks/useStyleSet'; - -const DownloadAttachment = ({ - activity: { attachments = [], channelData: { attachmentSizes = [] } = {} } = {}, - attachment -}) => { - const [{ downloadAttachment: downloadAttachmentStyleSet }] = useStyleSet(); - const downloadLabel = useLocalize('Download file'); - - const attachmentIndex = attachments.indexOf(attachment); - const size = attachmentSizes[attachmentIndex]; - const formattedSize = typeof size === 'number' && format(size); - - const downloadFileWithFileSizeLabel = useLocalize( - 'DownloadFileWithFileSize', - downloadLabel, - attachment.name, - formattedSize - ); - - return ( - - -
- - {/* Although nested, Chrome v75 does not respect the above aria-hidden and makes the below aria-hidden necessary */} -
-
{attachment.name}
-
{formattedSize}
-
- -
-
-
- ); -}; - -DownloadAttachment.propTypes = { - activity: PropTypes.shape({ - attachment: PropTypes.array, - channelData: PropTypes.shape({ - attachmentSizes: PropTypes.arrayOf(PropTypes.number) - }) - }).isRequired, - attachment: PropTypes.shape({ - contentUrl: PropTypes.string.isRequired, - name: PropTypes.string.isRequired - }).isRequired -}; - -export default DownloadAttachment; diff --git a/packages/component/src/Attachment/FileAttachment.js b/packages/component/src/Attachment/FileAttachment.js new file mode 100644 index 0000000000..61dc38bee5 --- /dev/null +++ b/packages/component/src/Attachment/FileAttachment.js @@ -0,0 +1,38 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +import FileContent from './FileContent'; +import useStyleSet from '../hooks/useStyleSet'; + +const FileAttachment = ({ + activity: { attachments = [], channelData: { attachmentSizes = [] } = {} } = {}, + attachment +}) => { + const [{ fileAttachment: fileAttachmentStyleSet }] = useStyleSet(); + const attachmentIndex = attachments.indexOf(attachment); + const size = attachmentSizes[attachmentIndex]; + + return ( + + ); +}; + +FileAttachment.propTypes = { + activity: PropTypes.shape({ + attachment: PropTypes.array, + channelData: PropTypes.shape({ + attachmentSizes: PropTypes.arrayOf(PropTypes.number) + }) + }).isRequired, + attachment: PropTypes.shape({ + contentUrl: PropTypes.string, + name: PropTypes.string.isRequired + }).isRequired +}; + +export default FileAttachment; diff --git a/packages/component/src/Attachment/FileContent.js b/packages/component/src/Attachment/FileContent.js new file mode 100644 index 0000000000..0c7daeada5 --- /dev/null +++ b/packages/component/src/Attachment/FileContent.js @@ -0,0 +1,106 @@ +import { css } from 'glamor'; +import { format } from 'bytes'; +import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React from 'react'; + +import DownloadIcon from './Assets/DownloadIcon'; +import ScreenReaderText from '../ScreenReaderText'; +import useLocalize from '../hooks/useLocalize'; +import useStyleSet from '../hooks/useStyleSet'; + +const ROOT_CSS = css({ + display: 'flex', + + '& .webchat__fileContent__buttonLink': { + display: 'flex', + flex: 1 + }, + + '& .webchat__fileContent__badge': { + display: 'flex', + flex: 1, + flexDirection: 'column' + } +}); + +const FileContentBadge = ({ downloadIcon, fileName, size }) => { + const formattedSize = typeof size === 'number' && format(size); + + return ( + +
+
{fileName}
+ {!!formattedSize &&
{formattedSize}
} +
+ {downloadIcon && } +
+ ); +}; + +FileContentBadge.defaultProps = { + downloadIcon: false, + size: undefined +}; + +FileContentBadge.propTypes = { + downloadIcon: PropTypes.bool, + fileName: PropTypes.string.isRequired, + size: PropTypes.number +}; + +const FileContent = ({ className, href, fileName, size }) => { + const formattedSize = format(size); + + const [{ fileContent: fileContentStyleSet }] = useStyleSet(); + + const downloadLabel = useLocalize('Download file'); + const uploadLabel = useLocalize('Upload file'); + const downloadFileWithFileSizeLabel = useLocalize('DownloadFileWithFileSize', downloadLabel, fileName, formattedSize); + const uploadFileWithFileSizeLabel = useLocalize('UploadFileWithFileSize', uploadLabel, fileName, formattedSize); + + const alt = href ? downloadFileWithFileSizeLabel : uploadFileWithFileSizeLabel; + + return ( +
+ {href ? ( + + + + {/* Although nested, Chrome v75 does not respect the above aria-hidden and makes the below aria-hidden in FileContentBadge necessary */} + + + + ) : ( + + + + + )} +
+ ); +}; + +FileContent.defaultProps = { + className: '', + href: undefined, + size: undefined +}; + +FileContent.propTypes = { + className: PropTypes.string, + fileName: PropTypes.string.isRequired, + href: PropTypes.string, + size: PropTypes.number +}; + +export default FileContent; diff --git a/packages/component/src/Attachment/ImageAttachment.js b/packages/component/src/Attachment/ImageAttachment.js index eadd2fc708..da8d47349a 100644 --- a/packages/component/src/Attachment/ImageAttachment.js +++ b/packages/component/src/Attachment/ImageAttachment.js @@ -11,11 +11,18 @@ ImageAttachment.propTypes = { activity: PropTypes.shape({ attachments: PropTypes.array.isRequired }).isRequired, - attachment: PropTypes.shape({ - contentUrl: PropTypes.string.isRequired, - name: PropTypes.string, - thumbnailUrl: PropTypes.string - }).isRequired + + // Either attachment.contentUrl or attachment.thumbnailUrl must be specified. + attachment: PropTypes.oneOfType([ + PropTypes.shape({ + contentUrl: PropTypes.string.isRequired, + name: PropTypes.string + }), + PropTypes.shape({ + name: PropTypes.string, + thumbnailUrl: PropTypes.string.isRequired + }) + ]).isRequired }; export default ImageAttachment; diff --git a/packages/component/src/Attachment/UploadAttachment.js b/packages/component/src/Attachment/UploadAttachment.js deleted file mode 100644 index 850e8f38b6..0000000000 --- a/packages/component/src/Attachment/UploadAttachment.js +++ /dev/null @@ -1,56 +0,0 @@ -import { css } from 'glamor'; -import { format } from 'bytes'; -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React from 'react'; - -import ScreenReaderText from '../ScreenReaderText'; -import useLocalize from '../hooks/useLocalize'; -import useStyleSet from '../hooks/useStyleSet'; - -const ROOT_CSS = css({ - display: 'flex', - flexDirection: 'column' -}); - -const UploadAttachment = ({ - activity: { attachments = [], channelData: { attachmentSizes = [] } = {} } = {}, - attachment -}) => { - const [{ uploadAttachment: uploadAttachmentStyleSet }] = useStyleSet(); - - const attachmentIndex = attachments.indexOf(attachment); - const uploadLabel = useLocalize('Upload file'); - const size = attachmentSizes[attachmentIndex]; - const formattedSize = typeof size === 'number' && format(size); - const uploadFileWithFileSizeLabel = useLocalize( - 'UploadFileWithFileSize', - uploadLabel, - attachment.name, - formattedSize - ); - - return ( - - -
-
{attachment.name}
-
{formattedSize}
-
-
- ); -}; - -UploadAttachment.propTypes = { - activity: PropTypes.shape({ - attachment: PropTypes.array, - channelData: PropTypes.shape({ - attachmentSizes: PropTypes.arrayOf(PropTypes.number) - }) - }).isRequired, - attachment: PropTypes.shape({ - name: PropTypes.string.isRequired - }).isRequired -}; - -export default UploadAttachment; diff --git a/packages/component/src/Localization/en-US.js b/packages/component/src/Localization/en-US.js index 5c0ddf883a..325668ab8e 100644 --- a/packages/component/src/Localization/en-US.js +++ b/packages/component/src/Localization/en-US.js @@ -92,7 +92,7 @@ export default { Total: 'Total', 'Type your message': 'Type your message', TypingIndicator: 'Showing typing indicator', - 'Upload file': 'Upload file', + 'Upload file': 'File', UploadFileWithFileSize: uploadFileWithFileSize, UserSent: 'User sent: ', VAT: 'VAT' diff --git a/packages/component/src/Middleware/Attachment/createCoreMiddleware.js b/packages/component/src/Middleware/Attachment/createCoreMiddleware.js index bdca8a7fe2..29bb401ee1 100644 --- a/packages/component/src/Middleware/Attachment/createCoreMiddleware.js +++ b/packages/component/src/Middleware/Attachment/createCoreMiddleware.js @@ -2,9 +2,8 @@ import PropTypes from 'prop-types'; import React from 'react'; import AudioAttachment from '../../Attachment/AudioAttachment'; -import DownloadAttachment from '../../Attachment/DownloadAttachment'; +import FileAttachment from '../../Attachment/FileAttachment'; import ImageAttachment from '../../Attachment/ImageAttachment'; -import UploadAttachment from '../../Attachment/UploadAttachment'; import TextAttachment from '../../Attachment/TextAttachment'; import VideoAttachment from '../../Attachment/VideoAttachment'; @@ -18,7 +17,7 @@ export default function createCoreMiddleware() { attachment: { contentType, contentUrl, thumbnailUrl } = {} }) => role === 'user' && !/^text\//u.test(contentType) && !thumbnailUrl ? ( - + ) : /^audio\//u.test(contentType) ? ( ) : /^image\//u.test(contentType) ? ( @@ -26,7 +25,7 @@ export default function createCoreMiddleware() { ) : /^video\//u.test(contentType) ? ( ) : contentUrl || contentType === 'application/octet-stream' ? ( - + ) : /^text\//u.test(contentType) ? ( ) : ( @@ -37,7 +36,8 @@ export default function createCoreMiddleware() { activity: PropTypes.any.isRequired, attachment: PropTypes.shape({ contentType: PropTypes.string.isRequired, - contentUrl: PropTypes.string.isRequired + contentUrl: PropTypes.string, + thumbnailUrl: PropTypes.string }).isRequired }; diff --git a/packages/component/src/Styles/StyleSet/DownloadAttachment.js b/packages/component/src/Styles/StyleSet/DownloadAttachment.js deleted file mode 100644 index bda90fb2a9..0000000000 --- a/packages/component/src/Styles/StyleSet/DownloadAttachment.js +++ /dev/null @@ -1,32 +0,0 @@ -export default function createDownloadAttachmentStyle({ accent, bubbleTextColor, paddingRegular, primaryFont }) { - return { - fontFamily: primaryFont, - - '& > a': { - alignItems: 'center', - color: bubbleTextColor, - // TODO: [P2] We should not set "display" in styleSet, this will allow the user to break the layout for no good reasons. - display: 'flex', - padding: paddingRegular, - textDecoration: 'none', - - '&:focus': { - backgroundColor: 'rgba(0, 0, 0, .1)' - }, - - '& > .icon': { - fill: accent, - marginLeft: paddingRegular, - padding: paddingRegular - }, - - '& > .details': { - flex: 1, - - '& > .name': { - color: accent - } - } - } - }; -} diff --git a/packages/component/src/Styles/StyleSet/FileAttachment.js b/packages/component/src/Styles/StyleSet/FileAttachment.js new file mode 100644 index 0000000000..7b84930165 --- /dev/null +++ b/packages/component/src/Styles/StyleSet/FileAttachment.js @@ -0,0 +1,3 @@ +export default function createFileAttachmentStyle() { + return {}; +} diff --git a/packages/component/src/Styles/StyleSet/FileContent.js b/packages/component/src/Styles/StyleSet/FileContent.js new file mode 100644 index 0000000000..fc71851c58 --- /dev/null +++ b/packages/component/src/Styles/StyleSet/FileContent.js @@ -0,0 +1,32 @@ +export default function createFileContentStyle({ accent, bubbleTextColor, paddingRegular, primaryFont }) { + return { + color: bubbleTextColor, + display: 'flex', + fontFamily: primaryFont, + padding: paddingRegular, + + '& .webchat__fileContent__badge': { + justifyContent: 'center' + }, + + '& .webchat__fileContent__buttonLink': { + alignItems: 'center', + color: bubbleTextColor, + textDecoration: 'none', + + '&:focus': { + backgroundColor: 'rgba(0, 0, 0, .1)' + } + }, + + '& .webchat__fileContent__downloadIcon': { + fill: accent, + marginLeft: paddingRegular, + padding: paddingRegular + }, + + '& .webchat__fileContent__fileName': { + color: accent + } + }; +} diff --git a/packages/component/src/Styles/StyleSet/UploadAttachment.js b/packages/component/src/Styles/StyleSet/UploadAttachment.js deleted file mode 100644 index e828eb72e7..0000000000 --- a/packages/component/src/Styles/StyleSet/UploadAttachment.js +++ /dev/null @@ -1,12 +0,0 @@ -export default function createUploadAttachmentStyle({ accent, bubbleTextColor, paddingRegular, primaryFont }) { - return { - color: bubbleTextColor, - fontFamily: primaryFont, - padding: paddingRegular, - textDecoration: 'none', - - '& > .name': { - color: accent - } - }; -} diff --git a/packages/component/src/Styles/createStyleSet.js b/packages/component/src/Styles/createStyleSet.js index 4f8aeebe52..79a794d342 100644 --- a/packages/component/src/Styles/createStyleSet.js +++ b/packages/component/src/Styles/createStyleSet.js @@ -9,9 +9,10 @@ import createCarouselFilmStrip from './StyleSet/CarouselFilmStrip'; import createCarouselFlipper from './StyleSet/CarouselFlipper'; import createConnectivityNotification from './StyleSet/ConnectivityNotification'; import createDictationInterimsStyle from './StyleSet/DictationInterims'; -import createDownloadAttachmentStyle from './StyleSet/DownloadAttachment'; import createErrorBoxStyle from './StyleSet/ErrorBox'; import createErrorNotificationStyle from './StyleSet/ErrorNotification'; +import createFileAttachmentStyle from './StyleSet/FileAttachment'; +import createFileContentStyle from './StyleSet/FileContent'; import createMicrophoneButtonStyle from './StyleSet/MicrophoneButton'; import createRootStyle from './StyleSet/Root'; import createScrollToEndButtonStyle from './StyleSet/ScrollToEndButton'; @@ -29,7 +30,6 @@ import createSuggestedActionStyle from './StyleSet/SuggestedAction'; import createTextContentStyle from './StyleSet/TextContent'; import createTypingAnimationStyle from './StyleSet/TypingAnimation'; import createTypingIndicatorStyle from './StyleSet/TypingIndicator'; -import createUploadAttachmentStyle from './StyleSet/UploadAttachment'; import createUploadButtonStyle from './StyleSet/UploadButton'; import createVideoAttachmentStyle from './StyleSet/VideoAttachment'; import createVideoContentStyle from './StyleSet/VideoContent'; @@ -179,9 +179,10 @@ export default function createStyleSet(options) { carouselFlipper: createCarouselFlipper(options), connectivityNotification: createConnectivityNotification(options), dictationInterims: createDictationInterimsStyle(options), - downloadAttachment: createDownloadAttachmentStyle(options), errorBox: createErrorBoxStyle(options), errorNotification: createErrorNotificationStyle(options), + fileAttachment: createFileAttachmentStyle(options), + fileContent: createFileContentStyle(options), microphoneButton: createMicrophoneButtonStyle(options), options: { ...options, @@ -202,7 +203,6 @@ export default function createStyleSet(options) { textContent: createTextContentStyle(options), typingAnimation: createTypingAnimationStyle(options), typingIndicator: createTypingIndicatorStyle(options), - uploadAttachment: createUploadAttachmentStyle(options), uploadButton: createUploadButtonStyle(options), videoAttachment: createVideoAttachmentStyle(options), videoContent: createVideoContentStyle(options), diff --git a/packages/component/src/index.tsx b/packages/component/src/index.tsx index b3dc84665b..060f2483e6 100644 --- a/packages/component/src/index.tsx +++ b/packages/component/src/index.tsx @@ -13,6 +13,7 @@ import StackedLayout, { connectStackedLayout } from './Activity/StackedLayout'; import Timestamp from './Middleware/ActivityStatus/Timestamp'; import AudioContent from './Attachment/AudioContent'; +import FileContent from './Attachment/FileContent'; import HTMLVideoContent from './Attachment/HTMLVideoContent'; import ImageContent from './Attachment/ImageContent'; import TextContent from './Attachment/TextContent'; @@ -47,6 +48,7 @@ const Components = { // Components for recomposing activities and attachments AudioContent, + FileContent, HTMLVideoContent, ImageContent, TextContent, diff --git a/packages/core/src/reducers/activities.js b/packages/core/src/reducers/activities.js index cce3206ec6..832c7b2519 100644 --- a/packages/core/src/reducers/activities.js +++ b/packages/core/src/reducers/activities.js @@ -11,6 +11,8 @@ import { POST_ACTIVITY_FULFILLED, POST_ACTIVITY_PENDING, POST_ACTIVITY_REJECTED import { SEND_FAILED, SENDING, SENT } from '../constants/ActivityClientState'; const DEFAULT_STATE = []; +const DIRECT_LINE_PLACEHOLDER_URL = + 'https://docs.botframework.com/static/devportal/client/images/bot-framework-default-placeholder.png'; function getClientActivityID({ channelData: { clientActivityID } = {} }) { return clientActivityID; @@ -20,7 +22,26 @@ function findByClientActivityID(clientActivityID) { return activity => getClientActivityID(activity) === clientActivityID; } +function patchActivity(activity) { + // Direct Line channel will return a placeholder image for the user-uploaded image. + // As observed, the URL for the placeholder image is https://docs.botframework.com/static/devportal/client/images/bot-framework-default-placeholder.png. + // To make our code simpler, we are removing the value if "contentUrl" is pointing to a placeholder image. + + // TODO: [P2] This "contentURL" removal code should be moved to DirectLineJS adapter. + + // Also, if the "contentURL" starts with "blob:", this means the user is uploading a file (the URL is constructed by URL.createObjectURL) + // Although the copy/reference of the file is temporary in-memory, to make the UX consistent across page refresh, we do not allow the user to re-download the file either. + + return updateIn(activity, ['attachments', () => true, 'contentUrl'], contentUrl => { + if (contentUrl !== DIRECT_LINE_PLACEHOLDER_URL && !/^blob:/iu.test(contentUrl)) { + return contentUrl; + } + }); +} + function upsertActivityWithSort(activities, nextActivity) { + nextActivity = patchActivity(nextActivity); + const { channelData: { clientActivityID: nextClientActivityID } = {} } = nextActivity; const nextTimestamp = Date.parse(nextActivity.timestamp); @@ -77,7 +98,7 @@ export default function activities(state = DEFAULT_STATE, { meta, payload, type case POST_ACTIVITY_FULFILLED: state = updateIn(state, [findByClientActivityID(meta.clientActivityID)], () => // We will replace the activity with the version from the server - updateIn(payload.activity, ['channelData', 'state'], () => SENT) + updateIn(patchActivity(payload.activity), ['channelData', 'state'], () => SENT) ); break; From f4f52a29d5849a17d308acc352b415835f8c69ac Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 03:51:47 -0800 Subject: [PATCH 02/10] Add entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1c1cecf8f..b1f118d06d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixes [#2822](https://github.com/microsoft/BotFramework-WebChat/issues/2822). Fixed `credentials` should return `authorizationToken` and `subscriptionKey` as string and allow empty LUIS reference grammar ID, by [@compulim](https://github.com/compulim) in PR [#2824](https://github.com/microsoft/BotFramework-WebChat/pull/2824) - Fixes [#2751](https://github.com/microsoft/BotFramework-WebChat/issues/2751). Move documentation to docs folder, by [@corinagum](https://github.com/corinagum) in PR [#2832](https://github.com/microsoft/BotFramework-WebChat/pull/2832) - Fixes [#2838](https://github.com/microsoft/BotFramework-WebChat/issues/2838). Fixed `concatMiddleware` should allow any middleware to call its downstream middleware twice, by [@compulim](https://github.com/compulim) in PR [#2839](https://github.com/microsoft/BotFramework-WebChat/pull/2839) +- Fixes [#2864](https://github.com/microsoft/BotFramework-WebChat/issues/2864). Replaced `DownloadAttachment` and `UploadAttachment` with `FileAttachment`, which show download link and icon if the attachment contains `contentUrl`, by [@compulim](https://github.com/compulim) in PR [#XXX](https://github.com/microsoft/BotFramework-WebChat/pull/XXX) ### Changed From 0636aa99df1cdd40c9b560d48a26ddee681ef38b Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 03:58:43 -0800 Subject: [PATCH 03/10] Add PR number --- packages/core/src/reducers/activities.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/reducers/activities.js b/packages/core/src/reducers/activities.js index 832c7b2519..a58fbc4d0e 100644 --- a/packages/core/src/reducers/activities.js +++ b/packages/core/src/reducers/activities.js @@ -27,7 +27,7 @@ function patchActivity(activity) { // As observed, the URL for the placeholder image is https://docs.botframework.com/static/devportal/client/images/bot-framework-default-placeholder.png. // To make our code simpler, we are removing the value if "contentUrl" is pointing to a placeholder image. - // TODO: [P2] This "contentURL" removal code should be moved to DirectLineJS adapter. + // TODO: [P2] #2869 This "contentURL" removal code should be moved to DirectLineJS adapter. // Also, if the "contentURL" starts with "blob:", this means the user is uploading a file (the URL is constructed by URL.createObjectURL) // Although the copy/reference of the file is temporary in-memory, to make the UX consistent across page refresh, we do not allow the user to re-download the file either. From 5f85b5ee42db45b04316f8e9af9f91b386aa3466 Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 12:40:42 -0800 Subject: [PATCH 04/10] Add optionals --- packages/component/src/Attachment/ImageAttachment.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/component/src/Attachment/ImageAttachment.js b/packages/component/src/Attachment/ImageAttachment.js index da8d47349a..99598f666f 100644 --- a/packages/component/src/Attachment/ImageAttachment.js +++ b/packages/component/src/Attachment/ImageAttachment.js @@ -16,9 +16,11 @@ ImageAttachment.propTypes = { attachment: PropTypes.oneOfType([ PropTypes.shape({ contentUrl: PropTypes.string.isRequired, - name: PropTypes.string + name: PropTypes.string, + thumbnailUrl: PropTypes.string }), PropTypes.shape({ + contentUrl: PropTypes.string, name: PropTypes.string, thumbnailUrl: PropTypes.string.isRequired }) From fd33b0c3eadab74d749f48bb5b9a432dbf3c77ef Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 17:32:47 -0800 Subject: [PATCH 05/10] Fix tests --- __tests__/attachmentMiddleware.js | 4 ++- __tests__/hooks/useSendFiles.js | 5 ++- __tests__/hooks/useSendPostBack.js | 2 +- __tests__/language.js | 2 +- __tests__/markdown.js | 2 +- __tests__/sendBox.js | 2 +- __tests__/sendTypingIndicator.js | 2 +- .../setup/conditions/minNumActivitiesShown.js | 18 ++++++++-- __tests__/speech.selectVoice.js | 6 ++-- __tests__/speech.synthesis.js | 4 +-- __tests__/styleOptions.js | 2 +- __tests__/suggestedActions.js | 4 +-- __tests__/timestamp.js | 6 ++-- __tests__/updateActivity.js | 4 +-- __tests__/upload.js | 35 ++++++++++++++----- 15 files changed, 68 insertions(+), 30 deletions(-) diff --git a/__tests__/attachmentMiddleware.js b/__tests__/attachmentMiddleware.js index ba08ca39dd..1821f2b20f 100644 --- a/__tests__/attachmentMiddleware.js +++ b/__tests__/attachmentMiddleware.js @@ -45,7 +45,9 @@ test('file upload should show thumbnail and file name', async () => { return next({ activity, attachment }); } - } + }, + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true }); await driver.wait(uiConnected(), timeouts.directLine); diff --git a/__tests__/hooks/useSendFiles.js b/__tests__/hooks/useSendFiles.js index 8784140635..d7b16d05b8 100644 --- a/__tests__/hooks/useSendFiles.js +++ b/__tests__/hooks/useSendFiles.js @@ -9,7 +9,10 @@ import uiConnected from '../setup/conditions/uiConnected'; jest.setTimeout(timeouts.test); test('calling sendFile should send files', async () => { - const { driver, pageObjects } = await setupWebDriver(); + const { driver, pageObjects } = await setupWebDriver({ + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true + }); await driver.wait(uiConnected(), timeouts.directLine); diff --git a/__tests__/hooks/useSendPostBack.js b/__tests__/hooks/useSendPostBack.js index 68908b0fb9..fee6eedc42 100644 --- a/__tests__/hooks/useSendPostBack.js +++ b/__tests__/hooks/useSendPostBack.js @@ -15,7 +15,7 @@ test('calling sendPostBack should send a post back activity', async () => { await pageObjects.runHook('useSendPostBack', [], sendPostBack => sendPostBack({ hello: 'World!' })); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(1), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); diff --git a/__tests__/language.js b/__tests__/language.js index 63504410ed..de223f9f67 100644 --- a/__tests__/language.js +++ b/__tests__/language.js @@ -14,7 +14,7 @@ test('setting language to empty string should not crash', async () => { await driver.wait(uiConnected(), timeouts.directLine); await pageObjects.sendMessageViaSendBox('echo Hello', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); diff --git a/__tests__/markdown.js b/__tests__/markdown.js index 4a22c5277a..95fa1ddd31 100644 --- a/__tests__/markdown.js +++ b/__tests__/markdown.js @@ -29,7 +29,7 @@ test('null renderMarkdown function', async () => { await pageObjects.sendMessageViaSendBox('echo **This text should be plain text**', { waitForSend: true }); await driver.wait(allImagesLoaded(), timeouts.fetchImage); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); diff --git a/__tests__/sendBox.js b/__tests__/sendBox.js index d8117558a8..b3485ec201 100644 --- a/__tests__/sendBox.js +++ b/__tests__/sendBox.js @@ -26,7 +26,7 @@ test('should focus send box when message is being sent', async () => { await driver.wait(uiConnected(), timeouts.directLine); await pageObjects.sendMessageViaSendBox('Hello, World!', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(1), timeouts.directLine); + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); diff --git a/__tests__/sendTypingIndicator.js b/__tests__/sendTypingIndicator.js index a7b5c43305..2870fe8b96 100644 --- a/__tests__/sendTypingIndicator.js +++ b/__tests__/sendTypingIndicator.js @@ -67,7 +67,7 @@ test('typing indicator should not display after second activity', async () => { }); await pageObjects.sendMessageViaSendBox('typing', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); diff --git a/__tests__/setup/conditions/minNumActivitiesShown.js b/__tests__/setup/conditions/minNumActivitiesShown.js index f708cfb7e5..bd26f3f15b 100644 --- a/__tests__/setup/conditions/minNumActivitiesShown.js +++ b/__tests__/setup/conditions/minNumActivitiesShown.js @@ -1,5 +1,19 @@ -import { By, until } from 'selenium-webdriver'; +import { Condition } from 'selenium-webdriver'; export default function minNumActivitiesShown(numActivities) { - return until.elementLocated(By.css(`[role="listitem"]:nth-child(${numActivities})`)); + return new Condition(`${numActivities} activities is shown`, async driver => { + // To run hooks (WebChatTest.runHook), the code internally create an activity. + // Inside the activity renderer, it call hooks, but return empty visually. + // This activity is invisible and should not count towards "minNumActivitiesShown". + + const numActivitiesShown = await driver.executeScript(() => + [].reduce.call( + document.querySelectorAll('[role="listitem"]'), + (numActivitiesShown, child) => numActivitiesShown + (child.children.length ? 1 : 0), + 0 + ) + ); + + return numActivitiesShown >= numActivities; + }); } diff --git a/__tests__/speech.selectVoice.js b/__tests__/speech.selectVoice.js index 92b0ac2d40..31e7e5a1f2 100644 --- a/__tests__/speech.selectVoice.js +++ b/__tests__/speech.selectVoice.js @@ -20,7 +20,7 @@ describe('selecting voice based on language', () => { await pageObjects.sendMessageViaMicrophone('echo 123'); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); await driver.wait(speechSynthesisUtterancePended(), timeouts.ui); await expect(pageObjects.startSpeechSynthesize()).resolves.toHaveProperty('voice', { @@ -42,7 +42,7 @@ describe('selecting voice based on language', () => { await pageObjects.sendMessageViaMicrophone('echo 123'); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); await driver.wait(speechSynthesisUtterancePended(), timeouts.ui); await expect(pageObjects.startSpeechSynthesize()).resolves.toHaveProperty('voice', { @@ -66,7 +66,7 @@ describe('selecting voice based on language', () => { await pageObjects.sendMessageViaMicrophone('echo 123'); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); await driver.wait(speechSynthesisUtterancePended(), timeouts.ui); await expect(pageObjects.startSpeechSynthesize()).resolves.toHaveProperty('voice', { diff --git a/__tests__/speech.synthesis.js b/__tests__/speech.synthesis.js index 52fbf582a3..40cbf82776 100644 --- a/__tests__/speech.synthesis.js +++ b/__tests__/speech.synthesis.js @@ -110,7 +110,7 @@ describe('speech synthesis', () => { await pageObjects.sendMessageViaMicrophone('echo Hello, World!'); await expect(speechRecognitionStartCalled().fn(driver)).resolves.toBeFalsy(); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); await expect(pageObjects.startSpeechSynthesize()).resolves.toHaveProperty( 'text', @@ -140,7 +140,7 @@ describe('speech synthesis', () => { } }); - await pageObjects.sendMessageViaMicrophone('input hint expected'); + await pageObjects.sendMessageViaMicrophone('hint expected'); await driver.wait(minNumActivitiesShown(2), timeouts.directLine); diff --git a/__tests__/styleOptions.js b/__tests__/styleOptions.js index 74ef198523..cd3c985a0a 100644 --- a/__tests__/styleOptions.js +++ b/__tests__/styleOptions.js @@ -35,7 +35,7 @@ describe('style options', () => { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); }); diff --git a/__tests__/suggestedActions.js b/__tests__/suggestedActions.js index 1c04c9a7b7..6fd20b402a 100644 --- a/__tests__/suggestedActions.js +++ b/__tests__/suggestedActions.js @@ -57,7 +57,7 @@ describe('suggested-actions command', () => { const imBackButton = buttons[1]; await imBackButton.click(); - await driver.wait(minNumActivitiesShown(3), timeouts.directLine); + await driver.wait(minNumActivitiesShown(4), timeouts.directLine); await driver.wait(allOutgoingActivitiesSent(), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); @@ -120,7 +120,7 @@ describe('suggested-actions command', () => { const postBackStringButton = buttons[4]; await postBackStringButton.click(); - await driver.wait(minNumActivitiesShown(3), timeouts.directLine); + await driver.wait(minNumActivitiesShown(4), timeouts.directLine); await driver.wait(allOutgoingActivitiesSent(), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); diff --git a/__tests__/timestamp.js b/__tests__/timestamp.js index 5bbb0ccc64..802a0e3ee1 100644 --- a/__tests__/timestamp.js +++ b/__tests__/timestamp.js @@ -24,7 +24,7 @@ test('update timestamp language on-the-fly', async () => { await driver.wait(uiConnected(), timeouts.directLine); await pageObjects.sendMessageViaSendBox('echo Hello, World!', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); @@ -83,7 +83,7 @@ test('prepend text', async () => { await driver.wait(uiConnected(), timeouts.directLine); await pageObjects.sendMessageViaSendBox('echo Hello, World!', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); }); @@ -94,7 +94,7 @@ test('change timestamp grouping on-the-fly', async () => { await driver.wait(uiConnected(), timeouts.directLine); await pageObjects.sendMessageViaSendBox('echo Hello, World!', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); expect(await driver.takeScreenshot()).toMatchImageSnapshot(imageSnapshotOptions); diff --git a/__tests__/updateActivity.js b/__tests__/updateActivity.js index c0c511eac2..78ca851aec 100644 --- a/__tests__/updateActivity.js +++ b/__tests__/updateActivity.js @@ -78,12 +78,12 @@ test('should not replace activity without activity ID', async () => { await pageObjects.sendMessageViaSendBox('echo This message will not be replaced by a carousel.', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(minNumActivitiesShown(3), timeouts.directLine); await expect(driver.takeScreenshot()).resolves.toMatchImageSnapshot(imageSnapshotOptions); await pageObjects.sendMessageViaSendBox('carousel', { waitForSend: true }); - await driver.wait(minNumActivitiesShown(3), timeouts.directLine); + await driver.wait(minNumActivitiesShown(5), timeouts.directLine); await driver.wait(allImagesLoaded(), timeouts.fetchImage); await driver.wait(scrollToBottomCompleted(), timeouts.scrollToBottom); diff --git a/__tests__/upload.js b/__tests__/upload.js index 7c7a589c34..30f4c6942a 100644 --- a/__tests__/upload.js +++ b/__tests__/upload.js @@ -11,7 +11,10 @@ jest.setTimeout(timeouts.test); describe('upload a picture', () => { test('', async () => { - const { driver, pageObjects } = await setupWebDriver(); + const { driver, pageObjects } = await setupWebDriver({ + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true + }); await driver.wait(uiConnected(), timeouts.directLine); @@ -32,7 +35,9 @@ describe('upload a picture', () => { uploadThumbnailHeight: 60, uploadThumbnailWidth: 120 } - } + }, + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true }); await driver.wait(uiConnected(), timeouts.directLine); @@ -52,7 +57,9 @@ describe('upload a picture', () => { styleOptions: { uploadThumbnailQuality: 0.1 } - } + }, + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true }); await driver.wait(uiConnected(), timeouts.directLine); @@ -72,7 +79,9 @@ describe('upload a picture', () => { styleOptions: { enableUploadThumbnail: false } - } + }, + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true }); await driver.wait(uiConnected(), timeouts.directLine); @@ -88,7 +97,10 @@ describe('upload a picture', () => { describe('without Web Worker', () => { test('', async () => { - const { driver, pageObjects } = await setupWebDriver(); + const { driver, pageObjects } = await setupWebDriver({ + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true + }); await driver.executeScript(() => { window.Worker = undefined; @@ -112,7 +124,9 @@ describe('upload a picture', () => { uploadThumbnailHeight: 60, uploadThumbnailWidth: 120 } - } + }, + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true }); await driver.executeScript(() => { @@ -135,7 +149,9 @@ describe('upload a picture', () => { styleOptions: { uploadThumbnailQuality: 0.1 } - } + }, + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true }); await driver.executeScript(() => { @@ -155,7 +171,10 @@ describe('upload a picture', () => { }); test('upload a ZIP file', async () => { - const { driver, pageObjects } = await setupWebDriver(); + const { driver, pageObjects } = await setupWebDriver({ + // TODO: [P3] Offline bot did not reply with a downloadable attachment, we need to use production bot + useProductionBot: true + }); await driver.wait(uiConnected(), timeouts.directLine); From d040f59fec315e774ba6c42dfd43ccbd08630a93 Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 19:12:36 -0800 Subject: [PATCH 06/10] Test for FileAttachment --- ...show-zip-files-with-content-url-1-snap.png | Bin 0 -> 15855 bytes ...w-zip-files-without-content-url-1-snap.png | Bin 0 -> 15266 bytes __tests__/fileAttachment.js | 96 ++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 __tests__/__image_snapshots__/chrome-docker/file-attachment-js-show-zip-files-with-content-url-1-snap.png create mode 100644 __tests__/__image_snapshots__/chrome-docker/file-attachment-js-show-zip-files-without-content-url-1-snap.png create mode 100644 __tests__/fileAttachment.js diff --git a/__tests__/__image_snapshots__/chrome-docker/file-attachment-js-show-zip-files-with-content-url-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/file-attachment-js-show-zip-files-with-content-url-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..c0a0e79f6b6df49cd86226507d96f99845fe4f9d GIT binary patch literal 15855 zcmeHuWmuGJ+wLI75*3yKC=yFS5fBh5X%!U)q!~JvA}t}!P{vXe2^mVd8HO07JCsto zkydbkVF-z#V_(nveLwf{?Qid6??3x^*Pk`V08iZab)VN6m+#b76;9Ey(4kPMQ)tC| znkW>F6$*9e`5#B&n|zi@Jp7NwSySOIirB$QMxp*fq3_+%_J~^;^YqXr{VZKx;tG3G zaQ5z*f=~XUw=X{mOMm|S#2@DfDR=Yf)-j)pmF$WGlo*S3{4noz_%0VcPx{JB$Mod1 z`NYdV>z@CuCx7>A0y<%Xp-oYb_R??E#!N+7TbzV*hw#}d=ashTSM}%34x>H=52mxD zP(S+q5JBk$+@^h={_hX}UW9*F!oN?!zfZycZ>OL|znf86Svev!^it`Fal{nObH2!4 zvbKjuSydIU+tc|QO&k{Tf$y@^=Sp6RvJ zZ5NdaC8eM@Zp743@g~anGNt&5;hUGcRn*kp~;<%bWMOW0?$^;pQXPB$GY5H%@CHUk`#OJ2XFj
>Zwjg4=RJqW2FB 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 k4jtOV literal 0 HcmV?d00001 diff --git a/__tests__/__image_snapshots__/chrome-docker/file-attachment-js-show-zip-files-without-content-url-1-snap.png b/__tests__/__image_snapshots__/chrome-docker/file-attachment-js-show-zip-files-without-content-url-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..4061132696dee261e4e8f41a23b592c9f589a127 GIT binary patch literal 15266 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

Hy*PwHKA-B9&E*PG@!`e^^AiFIZ z$2)+VZDR?ExrT^nM30#to6UIer4fstA30NNyx4joH8v`$r^upu6?Cb5Xc-8)fGq|& zueoWH#nda05UCSF3muDheJ6ja3)J8nRUT}Y>GZj^+{RmhvY__2gD?YDR=Qf|7ZeV zac*u0)Jttlk$@m9FLk9=bf;WTJuPi-P&fe}FtsW7=bxjw6>;(L_?jAXm=5{5xjIl1 zwSAqiaZ`A+eVu~1MXJ>~FJ|?^LnE}A&lyOM~`0#UlJOe-|-|sTBN1Mm;vt{yQ$`XFaFu}gXGgx(irg!wji5v)q06#x0 z5w-ql6v%t>WG+kyNF@qvr>GSSHc$0Cx2@7>hG41?f*5{?G{3e|p^r3qP1O~>Xq%3+W`a*93 z&;$$nY=9GB8Ixr71LmxSYvVD*SV1<|Qd3jmo}||UH#{045n!s!`?4Vk)zEMi!XpUV zMO|s7asbX0V)MSjH6`ZbJ$ZNr2c9jDwYOYOac`GRl(dNfDcKXWeO}jKPy>%&xS0xY z)pE!j`9IygA=rO z02Y^FLjwt^msdFenJgHU6pwCAWCaP9d3uYjlnC~{H?U>*zj4ZPu}MGZ|7E<$&ccH2 zp6PjiueB#fn|FS=)+{A(EdiW1#3qMzPCV?ExXC3a%DQJG02U(j4OD4|+4QqYe53Jf zDZ?_ybQOn8PEA8bN;~vvK;7dq-hJ|17%~C;;|C)V=65#OrFI>uSHP!vrT~-wTY{t- z$2lzna8zJF4%t(Jk-lPFL4h;hyMI3)d{oN{Z^&Y}4f-s;Sjv0BoP=)z0K9%d^414b z;A*FuA53uoGMSJr!m_d^U~otWsOH;z2w_6V(@>Rr9Q<#L=g+f)8L*ttOF zB9h%CrCoC%F3)0O`jFL9WT05q`Zd>DVZ(nvl@6Px8nBnC2Ye=+jXNJKvsnOCn86CFiZSVQ_1jQ7B<3Wump z7{5i};9CT3PMiy5*hIo*037pwXbyHGkmy{i`XI>SK|3Dqb}5dt8XJL@7X&X}Gy7Ix4{L#jtjX zCLPEudTXxq#`WtuDT;wVU1U~aq48^T^VBPbf~!|v;#qla`=ir1v_BvcL;7?^tP}tq zQtupiaI{V}UYL^o`Bs9)Mdo=3{RjR-gqy`P7HSfamSYLKGLSDb*>9bela@j_rrfHe z+ry4qA)62*NU#*diXy8UdO { + const { driver, pageObjects } = await setupWebDriver({ + createDirectLine: options => { + const directLine = window.WebChat.createDirectLine(options); + const patchedDirectLine = { + activity$: new Observable(observer => { + const subscription = directLine.activity$.subscribe({ + next(activity) { + observer.next( + Object.assign({}, activity, { + attachments: (activity.attachments || []).map(attachment => + Object.assign({}, attachment, { + contentUrl: 'http://example.org/' + }) + ) + }) + ); + } + }); + + return () => subscription.unsubscribe(); + }), + + connectionStatus$: directLine.connectionStatus$, + postActivity: directLine.postActivity.bind(directLine), + token: directLine.token + }; + + return patchedDirectLine; + } + }); + + await driver.wait(uiConnected(), timeouts.directLine); + + await pageObjects.sendFile('empty.zip'); + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), timeouts.fetchImage); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); + +test('show ZIP files without contentUrl', async () => { + const { driver, pageObjects } = await setupWebDriver({ + createDirectLine: options => { + const directLine = window.WebChat.createDirectLine(options); + const patchedDirectLine = { + activity$: new Observable(observer => { + const subscription = directLine.activity$.subscribe({ + next(activity) { + observer.next( + Object.assign({}, activity, { + attachments: (activity.attachments || []).map(attachment => + Object.assign({}, attachment, { + contentUrl: undefined + }) + ) + }) + ); + } + }); + + return () => subscription.unsubscribe(); + }), + + connectionStatus$: directLine.connectionStatus$, + postActivity: directLine.postActivity.bind(directLine), + token: directLine.token + }; + + return patchedDirectLine; + } + }); + + await driver.wait(uiConnected(), timeouts.directLine); + + await pageObjects.sendFile('empty.zip'); + await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allImagesLoaded(), timeouts.fetchImage); + + const base64PNG = await driver.takeScreenshot(); + + expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); +}); From b508c27f135f7f8269277f9ad4bd853fbb19d7b1 Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 29 Jan 2020 19:23:17 -0800 Subject: [PATCH 07/10] Also check for --- __tests__/fileAttachment.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/__tests__/fileAttachment.js b/__tests__/fileAttachment.js index 32382a82d0..feb080ced9 100644 --- a/__tests__/fileAttachment.js +++ b/__tests__/fileAttachment.js @@ -21,7 +21,7 @@ test('show ZIP files with contentUrl', async () => { Object.assign({}, activity, { attachments: (activity.attachments || []).map(attachment => Object.assign({}, attachment, { - contentUrl: 'http://example.org/' + contentUrl: 'https://example.org/' }) ) }) @@ -50,6 +50,17 @@ test('show ZIP files with contentUrl', async () => { const base64PNG = await driver.takeScreenshot(); expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); + + await expect( + driver.executeScript(() => + document.querySelector('[role="listitem"]:nth-child(1) a[target="_blank"]').getAttribute('href') + ) + ).resolves.toEqual('https://example.org/'); + await expect( + driver.executeScript(() => + document.querySelector('[role="listitem"]:nth-child(2) a[target="_blank"]').getAttribute('href') + ) + ).resolves.toEqual('https://example.org/'); }); test('show ZIP files without contentUrl', async () => { @@ -93,4 +104,11 @@ test('show ZIP files without contentUrl', async () => { const base64PNG = await driver.takeScreenshot(); expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); + + await expect( + driver.executeScript(() => !!document.querySelector('[role="listitem"]:nth-child(1) a')) + ).resolves.toBeFalsy(); + await expect( + driver.executeScript(() => !!document.querySelector('[role="listitem"]:nth-child(2) a')) + ).resolves.toBeFalsy(); }); From 3b230444e80d23f3396693205706f6e93bbb7114 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 30 Jan 2020 00:25:56 -0800 Subject: [PATCH 08/10] Test relliability --- __tests__/hooks/useSendFiles.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/__tests__/hooks/useSendFiles.js b/__tests__/hooks/useSendFiles.js index d7b16d05b8..52863f1c68 100644 --- a/__tests__/hooks/useSendFiles.js +++ b/__tests__/hooks/useSendFiles.js @@ -1,5 +1,6 @@ import { imageSnapshotOptions, timeouts } from '../constants.json'; +import allOutgoingActivitiesSent from '../setup/conditions/allOutgoingActivitiesSent'; import minNumActivitiesShown from '../setup/conditions/minNumActivitiesShown'; import uiConnected from '../setup/conditions/uiConnected'; @@ -27,6 +28,7 @@ test('calling sendFile should send files', async () => { }); await driver.wait(minNumActivitiesShown(2), timeouts.directLine); + await driver.wait(allOutgoingActivitiesSent(), timeouts.directLine); const base64PNG = await driver.takeScreenshot(); From 00364f764413b8d0dfbb7ebafd41e89bc5a1aab8 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 6 Feb 2020 16:03:37 +0800 Subject: [PATCH 09/10] Update CHANGELOG.md Co-Authored-By: TJ Durnford --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1f118d06d..df0ec62747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixes [#2822](https://github.com/microsoft/BotFramework-WebChat/issues/2822). Fixed `credentials` should return `authorizationToken` and `subscriptionKey` as string and allow empty LUIS reference grammar ID, by [@compulim](https://github.com/compulim) in PR [#2824](https://github.com/microsoft/BotFramework-WebChat/pull/2824) - Fixes [#2751](https://github.com/microsoft/BotFramework-WebChat/issues/2751). Move documentation to docs folder, by [@corinagum](https://github.com/corinagum) in PR [#2832](https://github.com/microsoft/BotFramework-WebChat/pull/2832) - Fixes [#2838](https://github.com/microsoft/BotFramework-WebChat/issues/2838). Fixed `concatMiddleware` should allow any middleware to call its downstream middleware twice, by [@compulim](https://github.com/compulim) in PR [#2839](https://github.com/microsoft/BotFramework-WebChat/pull/2839) -- Fixes [#2864](https://github.com/microsoft/BotFramework-WebChat/issues/2864). Replaced `DownloadAttachment` and `UploadAttachment` with `FileAttachment`, which show download link and icon if the attachment contains `contentUrl`, by [@compulim](https://github.com/compulim) in PR [#XXX](https://github.com/microsoft/BotFramework-WebChat/pull/XXX) +- Fixes [#2864](https://github.com/microsoft/BotFramework-WebChat/issues/2864). Replaced `DownloadAttachment` and `UploadAttachment` with `FileAttachment`, which shows the download link and icon if the attachment contains the `contentUrl`, by [@compulim](https://github.com/compulim) in PR [#2868](https://github.com/microsoft/BotFramework-WebChat/pull/2868) ### Changed From 68d53ba38b15218ecc3b3ffb240e935bb89558e1 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 6 Feb 2020 00:32:58 -0800 Subject: [PATCH 10/10] Apply PR comments --- .../src/Attachment/FileAttachment.js | 11 +------ .../component/src/Attachment/FileContent.js | 29 ++++++++----------- .../src/Styles/StyleSet/FileAttachment.js | 3 -- .../component/src/Styles/createStyleSet.js | 2 -- 4 files changed, 13 insertions(+), 32 deletions(-) delete mode 100644 packages/component/src/Styles/StyleSet/FileAttachment.js diff --git a/packages/component/src/Attachment/FileAttachment.js b/packages/component/src/Attachment/FileAttachment.js index 61dc38bee5..7fa45cadda 100644 --- a/packages/component/src/Attachment/FileAttachment.js +++ b/packages/component/src/Attachment/FileAttachment.js @@ -2,24 +2,15 @@ import PropTypes from 'prop-types'; import React from 'react'; import FileContent from './FileContent'; -import useStyleSet from '../hooks/useStyleSet'; const FileAttachment = ({ activity: { attachments = [], channelData: { attachmentSizes = [] } = {} } = {}, attachment }) => { - const [{ fileAttachment: fileAttachmentStyleSet }] = useStyleSet(); const attachmentIndex = attachments.indexOf(attachment); const size = attachmentSizes[attachmentIndex]; - return ( - - ); + return ; }; FileAttachment.propTypes = { diff --git a/packages/component/src/Attachment/FileContent.js b/packages/component/src/Attachment/FileContent.js index 0c7daeada5..bfcaae7f3e 100644 --- a/packages/component/src/Attachment/FileContent.js +++ b/packages/component/src/Attachment/FileContent.js @@ -66,25 +66,20 @@ const FileContent = ({ className, href, fileName, size }) => { aria-hidden={true} className={classNames('webchat__fileContent', ROOT_CSS + '', fileContentStyleSet + '', (className || '') + '')} > + {href ? ( - - - - {/* Although nested, Chrome v75 does not respect the above aria-hidden and makes the below aria-hidden in FileContentBadge necessary */} - - - + + {/* Although nested, Chrome v75 does not respect the above aria-hidden and makes the below aria-hidden in FileContentBadge necessary */} + + ) : ( - - - - + )} ); diff --git a/packages/component/src/Styles/StyleSet/FileAttachment.js b/packages/component/src/Styles/StyleSet/FileAttachment.js deleted file mode 100644 index 7b84930165..0000000000 --- a/packages/component/src/Styles/StyleSet/FileAttachment.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function createFileAttachmentStyle() { - return {}; -} diff --git a/packages/component/src/Styles/createStyleSet.js b/packages/component/src/Styles/createStyleSet.js index 79a794d342..f86b2f0a53 100644 --- a/packages/component/src/Styles/createStyleSet.js +++ b/packages/component/src/Styles/createStyleSet.js @@ -11,7 +11,6 @@ import createConnectivityNotification from './StyleSet/ConnectivityNotification' import createDictationInterimsStyle from './StyleSet/DictationInterims'; import createErrorBoxStyle from './StyleSet/ErrorBox'; import createErrorNotificationStyle from './StyleSet/ErrorNotification'; -import createFileAttachmentStyle from './StyleSet/FileAttachment'; import createFileContentStyle from './StyleSet/FileContent'; import createMicrophoneButtonStyle from './StyleSet/MicrophoneButton'; import createRootStyle from './StyleSet/Root'; @@ -181,7 +180,6 @@ export default function createStyleSet(options) { dictationInterims: createDictationInterimsStyle(options), errorBox: createErrorBoxStyle(options), errorNotification: createErrorNotificationStyle(options), - fileAttachment: createFileAttachmentStyle(options), fileContent: createFileContentStyle(options), microphoneButton: createMicrophoneButtonStyle(options), options: {