diff --git a/CHANGELOG.md b/CHANGELOG.md index e1f8113098..54bab96543 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,7 +112,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - Switched math block syntax from `$$` to Tex-style `\[ \]` and `\( \)` delimiters with improved rendering and error handling, in PR [#5353](https://github.com/microsoft/BotFramework-WebChat/pull/5353), by [@OEvgeny](https://github.com/OEvgeny) - Improved avatar display and grouping behavior by fixing rendering issues and activity sender identification, in PR [#5346](https://github.com/microsoft/BotFramework-WebChat/pull/5346), by [@OEvgeny](https://github.com/OEvgeny) - Activity "copy" button will use `outerHTML` and `textContent` for clipboard content, in PR [#5378](https://github.com/microsoft/BotFramework-WebChat/pull/5378), by [@compulim](https://github.com/compulim) -- Bumped dependencies to the latest versions, by [@compulim](https://github.com/compulim) in PR [#5385](https://github.com/microsoft/BotFramework-WebChat/pull/5385), [#5400](https://github.com/microsoft/BotFramework-WebChat/pull/5400), and [#5426](https://github.com/microsoft/BotFramework-WebChat/pull/5426) +- Bumped dependencies to the latest versions, by [@compulim](https://github.com/compulim) in PR [#5385](https://github.com/microsoft/BotFramework-WebChat/pull/5385), [#5400](https://github.com/microsoft/BotFramework-WebChat/pull/5400), [#5426](https://github.com/microsoft/BotFramework-WebChat/pull/5426), and [#5476](https://github.com/microsoft/BotFramework-WebChat/pull/5476) - Production dependencies - [`web-speech-cognitive-services@8.1.0`](https://npmjs.com/package/web-speech-cognitive-services) - [`react-dictate-button@4.0.0`](https://npmjs.com/package/react-dictate-button) @@ -135,7 +135,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - [`shiki@2.3.2`](https://npmjs.com/package/shiki/) - [`use-propagate@0.2.1`](https://npmjs.com/package/use-propagate/) - [`use-state-with-ref@0.1.0`](https://npmjs.com/package/use-state-with-ref/) - - [`valibot@0.42.1`](https://npmjs.com/package/valibot/) + - [`valibot@1.1.0`](https://npmjs.com/package/valibot/) - [`web-speech-cognitive-services@8.1.1`](https://npmjs.com/package/web-speech-cognitive-services/) - Development dependencies - [`@biomejs/biome@1.9.4`](https://npmjs.com/package/@biomejs/biome/) @@ -191,6 +191,7 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ - [`webpack-cli@6.0.1`](https://npmjs.com/package/webpack-cli/) - [`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) ### Fixed diff --git a/__tests__/bubbleNub.js b/__tests__/bubbleNub.js deleted file mode 100644 index ced8dcbbc5..0000000000 --- a/__tests__/bubbleNub.js +++ /dev/null @@ -1,216 +0,0 @@ -import { imageSnapshotOptions, timeouts } from './constants.json'; - -import allImagesLoaded from './setup/conditions/allImagesLoaded'; -import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown'; -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 - -jest.setTimeout(timeouts.test); - -async function sendMessageAndMatchSnapshot(driver, pageObjects, message) { - await driver.wait(uiConnected(), timeouts.directLine); - await pageObjects.sendMessageViaSendBox(message); - - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); - await driver.wait(allImagesLoaded(), timeouts.fetchImage); - - const base64PNG = await driver.takeScreenshot(); - - expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); -} - -describe('bubble nub', () => { - let props; - - test('with standard setup', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - styleOptions: { - bubbleNubOffset: 0, - bubbleNubSize: 10, - bubbleFromUserNubOffset: 0, - bubbleFromUserNubSize: 10 - } - }, - zoom: 3 - }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout carousel'); - }); - - beforeEach(() => { - props = { - styleOptions: { - bubbleBorderColor: 'red', - bubbleBorderRadius: 10, - bubbleBorderWidth: 2, - bubbleFromUserBorderColor: 'green', - bubbleFromUserBorderRadius: 10, - bubbleFromUserNubOffset: 0, - bubbleFromUserNubSize: 10, - bubbleFromUserBorderWidth: 2, - bubbleNubOffset: 0, - bubbleNubSize: 10 - } - }; - }); - - describe('with avatar initials', () => { - beforeEach(() => { - props = { - ...props, - styleOptions: { - ...props.styleOptions, - botAvatarInitials: 'WC', - userAvatarInitials: 'WW' - } - }; - }); - - test('and carousel with a message should have nub on message only', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout carousel'); - }); - - test('and carousel without a message should not have nubs', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'carousel'); - }); - - test('and stacked without a message should not have nubs', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single'); - }); - - test('and a single message should have nub', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - - test('and carousel with a single attachment should have nub on message only', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single carousel'); - }); - }); - - describe('without avatar initials', () => { - test('and carousel with a message should have nub on message only and indented', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout carousel'); - }); - - test('and carousel without a message should not have nubs and indented', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'carousel'); - }); - - test('and stacked without a message should not have nubs and indented', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single'); - }); - - test('and a single message should have nub and indented', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - - test('and carousel with a single attachment should have nub on message only', async () => { - const { driver, pageObjects } = await setupWebDriver({ props, zoom: 3 }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'layout single carousel'); - }); - }); - - describe('at corner with offset', () => { - test('of 5px should have corner radius of 5px', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - ...props, - styleOptions: { - ...props.styleOptions, - bubbleFromUserNubOffset: 5, - bubbleNubOffset: 5 - } - }, - zoom: 3 - }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - - test('of 10px should have corner radius of 10px', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - ...props, - styleOptions: { - ...props.styleOptions, - bubbleFromUserNubOffset: 10, - bubbleNubOffset: 10 - } - }, - zoom: 3 - }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - - test('of minus 5px should have corner radius of 5px and flipped nub', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - ...props, - styleOptions: { - ...props.styleOptions, - bubbleFromUserNubOffset: -5, - bubbleNubOffset: -5 - } - }, - zoom: 3 - }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - - test('of minus 10px should have corner radius of 10px and flipped nub', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - ...props, - styleOptions: { - ...props.styleOptions, - bubbleFromUserNubOffset: -10, - bubbleNubOffset: -10 - } - }, - zoom: 3 - }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - - test('at bottom should have flipped nub', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - ...props, - styleOptions: { - ...props.styleOptions, - bubbleFromUserNubOffset: 'bottom', - bubbleNubOffset: 'bottom' - } - }, - zoom: 3 - }); - - await sendMessageAndMatchSnapshot(driver, pageObjects, 'Hello, World!'); - }); - }); -}); diff --git a/__tests__/html/sendAttachmentOn/useSendFiles.deprecation.html b/__tests__/html/sendAttachmentOn/useSendFiles.deprecation.html index cc4e902e2d..bb9f7af2f2 100644 --- a/__tests__/html/sendAttachmentOn/useSendFiles.deprecation.html +++ b/__tests__/html/sendAttachmentOn/useSendFiles.deprecation.html @@ -29,8 +29,8 @@ await pageObjects.runHook(({ useSendFiles }) => useSendFiles()([fileBlob])); // THEN: It should send the file. - await pageConditions.allOutgoingActivitiesSent(); await pageConditions.numActivitiesShown(3); + await pageConditions.allOutgoingActivitiesSent(); await host.snapshot(); // THEN: Should print deprecation warning. diff --git a/__tests__/html/upload.image.html b/__tests__/html/upload.image.html index f180320d4d..083cf39c94 100644 --- a/__tests__/html/upload.image.html +++ b/__tests__/html/upload.image.html @@ -21,8 +21,8 @@ await pageConditions.uiConnected(); await pageObjects.uploadFile('seaofthieves.jpg'); - await pageConditions.allOutgoingActivitiesSent(); await pageConditions.numActivitiesShown(2); + await pageConditions.allOutgoingActivitiesSent(); await host.snapshot(); }); diff --git a/__tests__/html2/bubble/nub/carousel.html b/__tests__/html2/bubble/nub/carousel.html new file mode 100644 index 0000000000..f47193f47d --- /dev/null +++ b/__tests__/html2/bubble/nub/carousel.html @@ -0,0 +1,41 @@ + + +
+ + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-standard-setup-1-snap.png b/__tests__/html2/bubble/nub/carousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-standard-setup-1-snap.png rename to __tests__/html2/bubble/nub/carousel.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/offset/flipped.html b/__tests__/html2/bubble/nub/offset/flipped.html new file mode 100644 index 0000000000..df84d18ea6 --- /dev/null +++ b/__tests__/html2/bubble/nub/offset/flipped.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-at-bottom-should-have-flipped-nub-1-snap.png b/__tests__/html2/bubble/nub/offset/flipped.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-at-bottom-should-have-flipped-nub-1-snap.png rename to __tests__/html2/bubble/nub/offset/flipped.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/offset/negative10.html b/__tests__/html2/bubble/nub/offset/negative10.html new file mode 100644 index 0000000000..4066ddc89e --- /dev/null +++ b/__tests__/html2/bubble/nub/offset/negative10.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-minus-10-px-should-have-corner-radius-of-10-px-and-flipped-nub-1-snap.png b/__tests__/html2/bubble/nub/offset/negative10.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-minus-10-px-should-have-corner-radius-of-10-px-and-flipped-nub-1-snap.png rename to __tests__/html2/bubble/nub/offset/negative10.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/offset/negative5.html b/__tests__/html2/bubble/nub/offset/negative5.html new file mode 100644 index 0000000000..c40afc30bf --- /dev/null +++ b/__tests__/html2/bubble/nub/offset/negative5.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-minus-5-px-should-have-corner-radius-of-5-px-and-flipped-nub-1-snap.png b/__tests__/html2/bubble/nub/offset/negative5.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-minus-5-px-should-have-corner-radius-of-5-px-and-flipped-nub-1-snap.png rename to __tests__/html2/bubble/nub/offset/negative5.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/offset/positive10.html b/__tests__/html2/bubble/nub/offset/positive10.html new file mode 100644 index 0000000000..63be92e2be --- /dev/null +++ b/__tests__/html2/bubble/nub/offset/positive10.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-10-px-should-have-corner-radius-of-10-px-1-snap.png b/__tests__/html2/bubble/nub/offset/positive10.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-10-px-should-have-corner-radius-of-10-px-1-snap.png rename to __tests__/html2/bubble/nub/offset/positive10.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/offset/positive5.html b/__tests__/html2/bubble/nub/offset/positive5.html new file mode 100644 index 0000000000..3028e185fa --- /dev/null +++ b/__tests__/html2/bubble/nub/offset/positive5.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-5-px-should-have-corner-radius-of-5-px-1-snap.png b/__tests__/html2/bubble/nub/offset/positive5.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-at-corner-with-offset-of-5-px-should-have-corner-radius-of-5-px-1-snap.png rename to __tests__/html2/bubble/nub/offset/positive5.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withAvatars/carousel.html b/__tests__/html2/bubble/nub/withAvatars/carousel.html new file mode 100644 index 0000000000..7f0f66191b --- /dev/null +++ b/__tests__/html2/bubble/nub/withAvatars/carousel.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-carousel-without-a-message-should-not-have-nubs-1-snap.png b/__tests__/html2/bubble/nub/withAvatars/carousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-carousel-without-a-message-should-not-have-nubs-1-snap.png rename to __tests__/html2/bubble/nub/withAvatars/carousel.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withAvatars/helloWorld.html b/__tests__/html2/bubble/nub/withAvatars/helloWorld.html new file mode 100644 index 0000000000..3c35a2c649 --- /dev/null +++ b/__tests__/html2/bubble/nub/withAvatars/helloWorld.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-a-single-message-should-have-nub-1-snap.png b/__tests__/html2/bubble/nub/withAvatars/helloWorld.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-a-single-message-should-have-nub-1-snap.png rename to __tests__/html2/bubble/nub/withAvatars/helloWorld.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withAvatars/layoutCarousel.html b/__tests__/html2/bubble/nub/withAvatars/layoutCarousel.html new file mode 100644 index 0000000000..38b5002cf4 --- /dev/null +++ b/__tests__/html2/bubble/nub/withAvatars/layoutCarousel.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-carousel-with-a-message-should-have-nub-on-message-only-1-snap.png b/__tests__/html2/bubble/nub/withAvatars/layoutCarousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-carousel-with-a-message-should-have-nub-on-message-only-1-snap.png rename to __tests__/html2/bubble/nub/withAvatars/layoutCarousel.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withAvatars/layoutSingle.html b/__tests__/html2/bubble/nub/withAvatars/layoutSingle.html new file mode 100644 index 0000000000..7b4b50e6f7 --- /dev/null +++ b/__tests__/html2/bubble/nub/withAvatars/layoutSingle.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-stacked-without-a-message-should-not-have-nubs-1-snap.png b/__tests__/html2/bubble/nub/withAvatars/layoutSingle.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-stacked-without-a-message-should-not-have-nubs-1-snap.png rename to __tests__/html2/bubble/nub/withAvatars/layoutSingle.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withAvatars/layoutSingleCarousel.html b/__tests__/html2/bubble/nub/withAvatars/layoutSingleCarousel.html new file mode 100644 index 0000000000..4734e492a2 --- /dev/null +++ b/__tests__/html2/bubble/nub/withAvatars/layoutSingleCarousel.html @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-carousel-with-a-single-attachment-should-have-nub-on-message-only-1-snap.png b/__tests__/html2/bubble/nub/withAvatars/layoutSingleCarousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-with-avatar-initials-and-carousel-with-a-single-attachment-should-have-nub-on-message-only-1-snap.png rename to __tests__/html2/bubble/nub/withAvatars/layoutSingleCarousel.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withoutAvatars/carousel.html b/__tests__/html2/bubble/nub/withoutAvatars/carousel.html new file mode 100644 index 0000000000..bde584df72 --- /dev/null +++ b/__tests__/html2/bubble/nub/withoutAvatars/carousel.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-carousel-without-a-message-should-not-have-nubs-and-indented-1-snap.png b/__tests__/html2/bubble/nub/withoutAvatars/carousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-carousel-without-a-message-should-not-have-nubs-and-indented-1-snap.png rename to __tests__/html2/bubble/nub/withoutAvatars/carousel.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withoutAvatars/helloWorld.html b/__tests__/html2/bubble/nub/withoutAvatars/helloWorld.html new file mode 100644 index 0000000000..4453395cb7 --- /dev/null +++ b/__tests__/html2/bubble/nub/withoutAvatars/helloWorld.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-a-single-message-should-have-nub-and-indented-1-snap.png b/__tests__/html2/bubble/nub/withoutAvatars/helloWorld.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-a-single-message-should-have-nub-and-indented-1-snap.png rename to __tests__/html2/bubble/nub/withoutAvatars/helloWorld.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withoutAvatars/layoutCarousel.html b/__tests__/html2/bubble/nub/withoutAvatars/layoutCarousel.html new file mode 100644 index 0000000000..f30c186c73 --- /dev/null +++ b/__tests__/html2/bubble/nub/withoutAvatars/layoutCarousel.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-carousel-with-a-message-should-have-nub-on-message-only-and-indented-1-snap.png b/__tests__/html2/bubble/nub/withoutAvatars/layoutCarousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-carousel-with-a-message-should-have-nub-on-message-only-and-indented-1-snap.png rename to __tests__/html2/bubble/nub/withoutAvatars/layoutCarousel.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withoutAvatars/layoutSingle.html b/__tests__/html2/bubble/nub/withoutAvatars/layoutSingle.html new file mode 100644 index 0000000000..451d132165 --- /dev/null +++ b/__tests__/html2/bubble/nub/withoutAvatars/layoutSingle.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-stacked-without-a-message-should-not-have-nubs-and-indented-1-snap.png b/__tests__/html2/bubble/nub/withoutAvatars/layoutSingle.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-stacked-without-a-message-should-not-have-nubs-and-indented-1-snap.png rename to __tests__/html2/bubble/nub/withoutAvatars/layoutSingle.html.snap-1.png diff --git a/__tests__/html2/bubble/nub/withoutAvatars/layoutSingleCarousel.html b/__tests__/html2/bubble/nub/withoutAvatars/layoutSingleCarousel.html new file mode 100644 index 0000000000..b41d27aaf1 --- /dev/null +++ b/__tests__/html2/bubble/nub/withoutAvatars/layoutSingleCarousel.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + diff --git a/__tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-carousel-with-a-single-attachment-should-have-nub-on-message-only-1-snap.png b/__tests__/html2/bubble/nub/withoutAvatars/layoutSingleCarousel.html.snap-1.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/bubble-nub-js-bubble-nub-without-avatar-initials-and-carousel-with-a-single-attachment-should-have-nub-on-message-only-1-snap.png rename to __tests__/html2/bubble/nub/withoutAvatars/layoutSingleCarousel.html.snap-1.png diff --git a/__tests__/html2/simple.html b/__tests__/html2/simple.html new file mode 100644 index 0000000000..f39d6bea33 --- /dev/null +++ b/__tests__/html2/simple.html @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/__tests__/html2/simple.offline.html b/__tests__/html2/simple.offline.html new file mode 100644 index 0000000000..87339aeafb --- /dev/null +++ b/__tests__/html2/simple.offline.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/__tests__/html2/speech/bargeIn/behavior.html b/__tests__/html2/speech/bargeIn/behavior.html index 5e31da29f1..6cc84e3243 100644 --- a/__tests__/html2/speech/bargeIn/behavior.html +++ b/__tests__/html2/speech/bargeIn/behavior.html @@ -90,7 +90,7 @@ // AFTER: Microphone button is clicked. // THEN: Should construct a SpeechRecognition() instance. - expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1); + await waitFor(() => expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1)); const { value: speechRecognition1 } = ponyfill.SpeechRecognition.mock.results[0]; diff --git a/__tests__/html2/speech/inputHint.acceptingInput.html b/__tests__/html2/speech/inputHint.acceptingInput.html index e491c498d9..cee2439000 100644 --- a/__tests__/html2/speech/inputHint.acceptingInput.html +++ b/__tests__/html2/speech/inputHint.acceptingInput.html @@ -75,7 +75,7 @@ ); // THEN: Should construct the SpeechRecognition() instance and call start(). - expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1); + await waitFor(() => expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1)); const { value: speechRecognition1 } = ponyfill.SpeechRecognition.mock.results[0]; diff --git a/__tests__/html2/speech/inputHint.ignoringInput.html b/__tests__/html2/speech/inputHint.ignoringInput.html index a210f51ecf..117226b349 100644 --- a/__tests__/html2/speech/inputHint.ignoringInput.html +++ b/__tests__/html2/speech/inputHint.ignoringInput.html @@ -75,7 +75,7 @@ ); // THEN: Should construct the SpeechRecognition() instance and call start(). - expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1); + await waitFor(() => expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1)); const { value: speechRecognition1 } = ponyfill.SpeechRecognition.mock.results[0]; diff --git a/__tests__/html2/speech/performCardAction.interactive.html b/__tests__/html2/speech/performCardAction.interactive.html index 05d55ea472..947e897a6b 100644 --- a/__tests__/html2/speech/performCardAction.interactive.html +++ b/__tests__/html2/speech/performCardAction.interactive.html @@ -75,7 +75,7 @@ ); // THEN: Should construct the SpeechRecognition() instance and call start(). - expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1); + await waitFor(() => expect(ponyfill.SpeechRecognition).toHaveBeenCalledTimes(1)); const { value: speechRecognition1 } = ponyfill.SpeechRecognition.mock.results[0]; diff --git a/package-lock.json b/package-lock.json index 739984f787..67fdf94590 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24370,7 +24370,7 @@ "redux": "5.0.1", "simple-update-in": "2.2.0", "use-ref-from": "0.1.0", - "valibot": "0.42.1" + "valibot": "1.1.0" }, "devDependencies": { "@babel/cli": "^7.18.10", @@ -24428,6 +24428,20 @@ "dev": true, "license": "MIT" }, + "packages/api/node_modules/valibot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", + "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "packages/base": { "name": "botframework-webchat-base", "version": "0.0.0-0", @@ -24500,6 +24514,7 @@ "swiper": "8.4.7", "url-search-params-polyfill": "8.2.5", "uuid": "8.3.2", + "valibot": "1.1.0", "web-speech-cognitive-services": "8.1.1", "whatwg-fetch": "3.6.20" }, @@ -24699,6 +24714,20 @@ "dev": true, "license": "MIT" }, + "packages/bundle/node_modules/valibot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", + "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "packages/component": { "name": "botframework-webchat-component", "version": "0.0.0-0", @@ -24730,7 +24759,7 @@ "use-propagate": "0.2.1", "use-ref-from": "0.1.0", "use-state-with-ref": "0.1.0", - "valibot": "0.42.1" + "valibot": "1.1.0" }, "devDependencies": { "@babel/cli": "^7.18.10", @@ -24787,6 +24816,20 @@ "dev": true, "license": "MIT" }, + "packages/component/node_modules/valibot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", + "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "packages/core": { "name": "botframework-webchat-core", "version": "0.0.0-0", @@ -24800,7 +24843,7 @@ "redux": "5.0.1", "redux-saga": "1.3.0", "simple-update-in": "2.2.0", - "valibot": "0.42.1" + "valibot": "1.1.0" }, "devDependencies": { "@babel/cli": "^7.18.10", @@ -24996,6 +25039,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/core/node_modules/valibot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", + "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "packages/directlinespeech": { "name": "botframework-directlinespeech-sdk", "version": "0.0.0-0", @@ -26198,7 +26255,7 @@ "inject-meta-tag": "0.0.1", "math-random": "2.0.1", "use-ref-from": "0.1.0", - "valibot": "0.42.1" + "valibot": "1.1.0" }, "devDependencies": { "@tsconfig/strictest": "^2.0.5", @@ -26231,6 +26288,20 @@ "dev": true, "license": "MIT" }, + "packages/fluent-theme/node_modules/valibot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.1.0.tgz", + "integrity": "sha512-Nk8lX30Qhu+9txPYTwM0cFlWLdPFsFr6LblzqIySfbZph9+BFsAHsNvHOymEviUepeIW6KFHzpX8TKhbptBXXw==", + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "packages/isomorphic-react": { "version": "0.0.0-0", "license": "MIT", diff --git a/packages/api/package.json b/packages/api/package.json index 2b30b169fe..14ab024f9c 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -101,10 +101,6 @@ "react-redux": [ "7", "react-redux@>7 requires newer version of React" - ], - "valibot": [ - "0", - "valibot@0 until they finalize @1" ] }, "devDependencies": { @@ -139,7 +135,7 @@ "redux": "5.0.1", "simple-update-in": "2.2.0", "use-ref-from": "0.1.0", - "valibot": "0.42.1" + "valibot": "1.1.0" }, "peerDependencies": { "react": ">= 16.8.6", diff --git a/packages/api/src/hooks/Composer.tsx b/packages/api/src/hooks/Composer.tsx index 929a1b2355..eba1563eb3 100644 --- a/packages/api/src/hooks/Composer.tsx +++ b/packages/api/src/hooks/Composer.tsx @@ -221,7 +221,14 @@ type ComposerCoreProps = Readonly<{ ) => Promise{speak};
-};
-
-SayAlt.defaultProps = {
- speak: ''
-};
-
-SayAlt.propTypes = {
- speak: PropTypes.string
-};
-
-export default SayAlt;
diff --git a/packages/component/src/Activity/SayAlt.tsx b/packages/component/src/Activity/SayAlt.tsx
new file mode 100644
index 0000000000..c944cb892a
--- /dev/null
+++ b/packages/component/src/Activity/SayAlt.tsx
@@ -0,0 +1,30 @@
+import { validateProps } from 'botframework-webchat-api/internal';
+import React, { memo } from 'react';
+import { literal, object, pipe, readonly, string, union, type InferInput } from 'valibot';
+
+import { useStyleToEmotionObject } from '../hooks/internal/styleToEmotionObject';
+
+// TODO: [P3] Although this is for development purpose, prettify it
+const ROOT_STYLE = {
+ color: 'Red',
+ margin: 0
+};
+
+const sayAltPropsSchema = pipe(
+ object({
+ speak: union([literal(false), string()])
+ }),
+ readonly()
+);
+
+type SayAltProps = InferInput{speak};
+};
+
+export default memo(SayAlt);
+export { sayAltPropsSchema, type SayAltProps };
diff --git a/packages/component/src/Activity/Speak.tsx b/packages/component/src/Activity/Speak.tsx
index e28d6fad40..ca5386ee31 100644
--- a/packages/component/src/Activity/Speak.tsx
+++ b/packages/component/src/Activity/Speak.tsx
@@ -1,8 +1,10 @@
import { hooks } from 'botframework-webchat-api';
-import type { WebChatActivity } from 'botframework-webchat-core';
-import PropTypes from 'prop-types';
-import React, { FC, memo, useCallback, useMemo } from 'react';
+import { validateProps } from 'botframework-webchat-api/internal';
+import { type WebChatActivity } from 'botframework-webchat-core';
+import React, { memo, useCallback, useMemo } from 'react';
import ReactSay, { SayUtterance } from 'react-say';
+import { useRefFrom } from 'use-ref-from';
+import { any, array, object, optional, pipe, readonly, string, type InferInput } from 'valibot';
import SayAlt from './SayAlt';
@@ -13,18 +15,62 @@ const { useMarkActivityAsSpoken, useStyleOptions, useVoiceSelector } = hooks;
// TODO: [P4] Consider moving this feature into BasicActivity
// And it has better DOM position for showing visual spoken text
-type SpeakProps = {
- activity: WebChatActivity;
-};
+const speakableActivitySchema = pipe(
+ object({
+ attachments: optional(
+ pipe(
+ array(
+ pipe(
+ object({
+ content: optional(any()),
+ contentType: string(),
+ speak: optional(string()),
+ subtitle: optional(string()),
+ text: optional(string()),
+ title: optional(string())
+ }),
+ readonly()
+ )
+ ),
+ readonly()
+ )
+ ),
+ channelData: optional(
+ pipe(
+ object({
+ speechSynthesisUtterance: optional(any())
+ }),
+ readonly()
+ )
+ ),
+ speak: optional(string()),
+ text: optional(string()),
+ type: string()
+ }),
+ readonly()
+);
+
+const speakPropsSchema = pipe(
+ object({
+ activity: speakableActivitySchema
+ }),
+ readonly()
+);
+
+type SpeakProps = InferInput- {suggestedActions.actions.map((action, index) => ( - // Direct Line schema does not have key other than index. - // eslint-disable-next-line react/no-array-index-key - - ))} -
- ); +function LiveRegionSuggestedActions(props: LiveRegionSuggestedActionsProps) { + const { suggestedActions } = validateProps(liveRegionSuggestedActionsPropSchema, props); -LiveRegionSuggestedActions.propTypes = { - suggestedActions: PropTypes.shape({ - actions: PropTypes.array - }).isRequired -}; + return ( + suggestedActions.actions?.length && ( ++ {suggestedActions.actions.map((action, index) => ( + // Direct Line schema does not have key other than index. + // eslint-disable-next-line react/no-array-index-key + + ))} +
+ ) + ); +} -export default LiveRegionSuggestedActions; +export default memo(LiveRegionSuggestedActions); +export { liveRegionSuggestedActionsPropSchema, type LiveRegionSuggestedActionsProps }; diff --git a/packages/component/src/Middleware/Avatar/createCoreMiddleware.tsx b/packages/component/src/Middleware/Avatar/createCoreMiddleware.tsx index 07863132f0..712bf40aba 100644 --- a/packages/component/src/Middleware/Avatar/createCoreMiddleware.tsx +++ b/packages/component/src/Middleware/Avatar/createCoreMiddleware.tsx @@ -1,12 +1,13 @@ import { AvatarMiddleware } from 'botframework-webchat-api'; +import { validateProps } from 'botframework-webchat-api/internal'; import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { FC } from 'react'; +import React, { memo } from 'react'; +import { boolean, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; import ImageAvatar from '../../Avatar/ImageAvatar'; import InitialsAvatar from '../../Avatar/InitialsAvatar'; -import useStyleSet from '../../hooks/useStyleSet'; import { useStyleToEmotionObject } from '../../hooks/internal/styleToEmotionObject'; +import useStyleSet from '../../hooks/useStyleSet'; const ROOT_STYLE = { overflow: ['hidden', 'clip'], @@ -19,13 +20,20 @@ const ROOT_STYLE = { } }; -type DefaultAvatarProps = { - 'aria-hidden'?: boolean; - className?: string; - fromUser: boolean; -}; +const defaultAvatarPropsSchema = pipe( + object({ + 'aria-hidden': optional(boolean()), + className: optional(string()), + fromUser: boolean() + }), + readonly() +); + +type DefaultAvatarProps = InferInput
{dictateState === STARTING && localize('SPEECH_INPUT_STARTING')}
@@ -43,7 +51,7 @@ const DictationInterims: FC
{dictateInterims.map((interim, index) => (
@@ -55,24 +63,17 @@ const DictationInterims: FC
{localize('SPEECH_INPUT_LISTENING')}