diff --git a/.github/workflows/pull-request-validation.yml b/.github/workflows/pull-request-validation.yml index 680f4b05fa..805d46b1e7 100644 --- a/.github/workflows/pull-request-validation.yml +++ b/.github/workflows/pull-request-validation.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - if: "env.skip-secure-feed != 'true'" name: Enable secure feed @@ -39,7 +39,7 @@ jobs: timeout-minutes: 8 # Terrapin normally finish within 4 minutes but sometimes hang - name: Use Node.js ${{ env.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ env.node-version }} cache: npm @@ -74,7 +74,7 @@ jobs: - run: ls -l docker.zip - name: Upload Docker artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 with: name: docker path: docker.zip @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - if: "env.skip-secure-feed != 'true'" name: Enable secure feed @@ -93,7 +93,7 @@ jobs: timeout-minutes: 8 # Terrapin normally finish within 4 minutes but sometimes hang - name: Use Node.js ${{ env.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ env.node-version }} cache: npm @@ -111,7 +111,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - if: "env.skip-secure-feed != 'true'" name: Enable secure feed @@ -119,7 +119,7 @@ jobs: timeout-minutes: 8 # Terrapin normally finish within 4 minutes but sometimes hang - name: Use Node.js ${{ env.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ env.node-version }} cache: npm @@ -154,7 +154,7 @@ jobs: - if: always() name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 with: name: test-result path: | @@ -168,7 +168,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - if: "env.skip-secure-feed != 'true'" name: Enable secure feed @@ -176,7 +176,7 @@ jobs: timeout-minutes: 8 # Terrapin normally finish within 4 minutes but sometimes hang - name: Use Node.js ${{ env.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ env.node-version }} cache: npm @@ -221,12 +221,12 @@ jobs: timeout-minutes: 8 # Terrapin normally finish within 4 minutes but sometimes hang - name: Use Node.js ${{ env.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: ${{ env.node-version }} - name: Download Docker artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v8 with: name: docker @@ -237,10 +237,10 @@ jobs: - run: npm clean-install - name: Run docker-compose build - run: docker-compose -f docker-compose-wsl2.yml build --build-arg REGISTRY=mcr.microsoft.com/mirror/docker/library + run: docker compose -f docker-compose-wsl2.yml build --build-arg REGISTRY=mcr.microsoft.com - name: Run docker-compose up - run: docker-compose -f docker-compose-wsl2.yml up --detach --scale chrome=2 + run: docker compose -f docker-compose-wsl2.yml up --detach --scale chrome=2 - name: Wait for Docker to be ready run: | @@ -269,7 +269,7 @@ jobs: - if: always() name: Print Docker logs - run: docker-compose -f docker-compose-wsl2.yml logs + run: docker compose -f docker-compose-wsl2.yml logs - if: always() name: Append ID to test result @@ -284,7 +284,7 @@ jobs: - if: always() name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 with: name: test-result path: | @@ -295,7 +295,7 @@ jobs: - if: failure() name: Upload test snapshot diffs - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v7 with: name: test-snapshot-diff path: ./__tests__/__image_snapshots__/*/__diff_output__/* @@ -310,7 +310,7 @@ jobs: steps: - name: Download test results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v8 with: name: test-result diff --git a/CHANGELOG.md b/CHANGELOG.md index a5f6f8c216..4589509d93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,18 @@ Notes: web developers are advised to use [`~` (tilde range)](https://github.com/ ## [Unreleased] +## [4.18.1] - 2026-05-13 + +### Changed + +- Bumped some dependencies to the latest versions, by [@compulim](https://github.com/compulim) in PR [#XXX](https://github.com/microsoft/BotFramework-WebChat/pull/XXX) + - Production dependencies + - [`adaptivecards@3.0.6`](https://npmjs.com/package/adaptivecards) + - [`swiper@12.1.4`](https://npmjs.com/package/swiper) + - Transient dependencies + - [`form-data@4.0.6`](https://npmjs.com/package/form-data) + - [`handlebars@4.7.9`](https://npmjs.com/package/handlebars) + ## [4.18.0] - 2024-07-10 ### Added diff --git a/__tests__/__image_snapshots__/html/auto-scroll-after-send-js-auto-scroll-should-scroll-to-bottom-on-send-1-snap.png b/__tests__/__image_snapshots__/html/auto-scroll-after-send-js-auto-scroll-should-scroll-to-bottom-on-send-1-snap.png index 8543561d96..7adf6440c0 100644 Binary files a/__tests__/__image_snapshots__/html/auto-scroll-after-send-js-auto-scroll-should-scroll-to-bottom-on-send-1-snap.png and b/__tests__/__image_snapshots__/html/auto-scroll-after-send-js-auto-scroll-should-scroll-to-bottom-on-send-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/chat-adapter-direct-line-app-service-extension-js-direct-line-app-service-service-chat-adapter-should-connect-to-the-mock-bot-1-snap.png b/__tests__/__image_snapshots__/html/chat-adapter-direct-line-app-service-extension-js-direct-line-app-service-service-chat-adapter-should-connect-to-the-mock-bot-1-snap.png index 76ee65ac0e..3f3c251d51 100644 Binary files a/__tests__/__image_snapshots__/html/chat-adapter-direct-line-app-service-extension-js-direct-line-app-service-service-chat-adapter-should-connect-to-the-mock-bot-1-snap.png and b/__tests__/__image_snapshots__/html/chat-adapter-direct-line-app-service-extension-js-direct-line-app-service-service-chat-adapter-should-connect-to-the-mock-bot-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/conversation-start-properties-no-locale-is-sent-js-conversation-start-properties-with-no-locale-is-sent-should-get-hello-and-welcome-message-1-snap.png b/__tests__/__image_snapshots__/html/conversation-start-properties-no-locale-is-sent-js-conversation-start-properties-with-no-locale-is-sent-should-get-hello-and-welcome-message-1-snap.png index 60e934cd50..712f13b2b8 100644 Binary files a/__tests__/__image_snapshots__/html/conversation-start-properties-no-locale-is-sent-js-conversation-start-properties-with-no-locale-is-sent-should-get-hello-and-welcome-message-1-snap.png and b/__tests__/__image_snapshots__/html/conversation-start-properties-no-locale-is-sent-js-conversation-start-properties-with-no-locale-is-sent-should-get-hello-and-welcome-message-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/conversation-start-properties-send-en-us-js-conversation-start-properties-with-locale-of-en-us-should-get-hello-and-welcome-message-1-snap.png b/__tests__/__image_snapshots__/html/conversation-start-properties-send-en-us-js-conversation-start-properties-with-locale-of-en-us-should-get-hello-and-welcome-message-1-snap.png index 60e934cd50..70325cd939 100644 Binary files a/__tests__/__image_snapshots__/html/conversation-start-properties-send-en-us-js-conversation-start-properties-with-locale-of-en-us-should-get-hello-and-welcome-message-1-snap.png and b/__tests__/__image_snapshots__/html/conversation-start-properties-send-en-us-js-conversation-start-properties-with-locale-of-en-us-should-get-hello-and-welcome-message-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/conversation-start-properties-send-invalid-type-js-conversation-start-properties-with-locale-of-invalid-type-should-get-hello-and-welcome-message-1-snap.png b/__tests__/__image_snapshots__/html/conversation-start-properties-send-invalid-type-js-conversation-start-properties-with-locale-of-invalid-type-should-get-hello-and-welcome-message-1-snap.png index 60e934cd50..712f13b2b8 100644 Binary files a/__tests__/__image_snapshots__/html/conversation-start-properties-send-invalid-type-js-conversation-start-properties-with-locale-of-invalid-type-should-get-hello-and-welcome-message-1-snap.png and b/__tests__/__image_snapshots__/html/conversation-start-properties-send-invalid-type-js-conversation-start-properties-with-locale-of-invalid-type-should-get-hello-and-welcome-message-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-existing-js-conversation-start-properties-with-non-existing-locale-should-get-hello-and-welcome-message-1-snap.png b/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-existing-js-conversation-start-properties-with-non-existing-locale-should-get-hello-and-welcome-message-1-snap.png index 60e934cd50..7c662e4c4c 100644 Binary files a/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-existing-js-conversation-start-properties-with-non-existing-locale-should-get-hello-and-welcome-message-1-snap.png and b/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-existing-js-conversation-start-properties-with-non-existing-locale-should-get-hello-and-welcome-message-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-iso-format-js-conversation-start-properties-with-non-iso-format-locale-should-get-hello-and-welcome-message-1-snap.png b/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-iso-format-js-conversation-start-properties-with-non-iso-format-locale-should-get-hello-and-welcome-message-1-snap.png index 60e934cd50..3ee4fca9ac 100644 Binary files a/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-iso-format-js-conversation-start-properties-with-non-iso-format-locale-should-get-hello-and-welcome-message-1-snap.png and b/__tests__/__image_snapshots__/html/conversation-start-properties-send-non-iso-format-js-conversation-start-properties-with-non-iso-format-locale-should-get-hello-and-welcome-message-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/conversation-start-properties-send-zh-cn-js-conversation-start-properties-with-locale-of-zh-cn-should-get-hello-and-welcome-in-simplified-chinese-1-snap.png b/__tests__/__image_snapshots__/html/conversation-start-properties-send-zh-cn-js-conversation-start-properties-with-locale-of-zh-cn-should-get-hello-and-welcome-in-simplified-chinese-1-snap.png index 029b279549..3bd852d2df 100644 Binary files a/__tests__/__image_snapshots__/html/conversation-start-properties-send-zh-cn-js-conversation-start-properties-with-locale-of-zh-cn-should-get-hello-and-welcome-in-simplified-chinese-1-snap.png and b/__tests__/__image_snapshots__/html/conversation-start-properties-send-zh-cn-js-conversation-start-properties-with-locale-of-zh-cn-should-get-hello-and-welcome-in-simplified-chinese-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-1-snap.png b/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-1-snap.png index c8f71ff439..a009002347 100644 Binary files a/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-1-snap.png and b/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-2-snap.png b/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-2-snap.png index c5bbd56334..83765849fa 100644 Binary files a/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-2-snap.png and b/__tests__/__image_snapshots__/html/hooks-use-text-box-js-use-text-box-should-work-properly-2-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/card-action-middleware-js-card-action-open-url-1-snap.png b/__tests__/__image_snapshots__/html/open-url-js-card-action-open-url-html-1-snap.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/card-action-middleware-js-card-action-open-url-1-snap.png rename to __tests__/__image_snapshots__/html/open-url-js-card-action-open-url-html-1-snap.png diff --git a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-1-snap.png b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-1-snap.png index 69ef126a81..3afb91d1c6 100644 Binary files a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-1-snap.png and b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-1-snap.png differ diff --git a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-2-snap.png b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-2-snap.png index c6527b33a2..043f09ea43 100644 Binary files a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-2-snap.png and b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-2-snap.png differ diff --git a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-3-snap.png b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-3-snap.png index 7036d9dd48..6b1caabef4 100644 Binary files a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-3-snap.png and b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-3-snap.png differ diff --git a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-4-snap.png b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-4-snap.png index c8f71ff439..3519fcb555 100644 Binary files a/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-4-snap.png and b/__tests__/__image_snapshots__/html/scroll-to-end-button-visibility-js-scroll-to-end-button-should-show-and-hide-properly-4-snap.png differ diff --git a/__tests__/__image_snapshots__/chrome-docker/card-action-middleware-js-card-action-signin-1-snap.png b/__tests__/__image_snapshots__/html/sign-in-js-card-action-sign-in-html-1-snap.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/card-action-middleware-js-card-action-signin-1-snap.png rename to __tests__/__image_snapshots__/html/sign-in-js-card-action-sign-in-html-1-snap.png diff --git a/__tests__/__image_snapshots__/chrome-docker/card-action-middleware-js-card-action-signin-when-direct-line-get-session-id-is-falsy-1-snap.png b/__tests__/__image_snapshots__/html/sign-in-no-get-session-id-js-card-action-sign-in-no-get-session-id-html-1-snap.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/card-action-middleware-js-card-action-signin-when-direct-line-get-session-id-is-falsy-1-snap.png rename to __tests__/__image_snapshots__/html/sign-in-no-get-session-id-js-card-action-sign-in-no-get-session-id-html-1-snap.png diff --git a/__tests__/__image_snapshots__/chrome-docker/video-js-video-1-snap.png b/__tests__/__image_snapshots__/html/video-youtube-js-1-snap.png similarity index 100% rename from __tests__/__image_snapshots__/chrome-docker/video-js-video-1-snap.png rename to __tests__/__image_snapshots__/html/video-youtube-js-1-snap.png diff --git a/__tests__/cardActionMiddleware.js b/__tests__/cardActionMiddleware.js deleted file mode 100644 index ceaafdfa55..0000000000 --- a/__tests__/cardActionMiddleware.js +++ /dev/null @@ -1,156 +0,0 @@ -import { By } from 'selenium-webdriver'; - -import { imageSnapshotOptions, timeouts } from './constants.json'; - -import allOutgoingActivitiesSent from './setup/conditions/allOutgoingActivitiesSent'; -import getTranscript from './setup/elements/getTranscript'; -import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown.js'; -import suggestedActionsShown from './setup/conditions/suggestedActionsShown'; -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); - -test('card action "openUrl"', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - cardActionMiddleware: - ({ dispatch }) => - next => - ({ cardAction }) => { - if (cardAction.type === 'openUrl') { - dispatch({ - type: 'WEB_CHAT/SEND_MESSAGE', - payload: { - text: `Navigating to ${cardAction.value}` - } - }); - } else { - return next(cardAction); - } - } - } - }); - - await driver.wait(uiConnected(), timeouts.directLine); - await pageObjects.sendMessageViaSendBox('card-actions', { waitForSend: true }); - - await driver.wait(suggestedActionsShown(), timeouts.directLine); - - const openUrlButton = await driver.findElement(By.css('[role="form"] ul > li:first-child button')); - - await openUrlButton.click(); - await driver.wait(minNumActivitiesShown(4), timeouts.directLine); - await driver.wait(allOutgoingActivitiesSent(), timeouts.directLine); - - const base64PNG = await driver.takeScreenshot(); - - expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); -}); - -test('card action "signin"', async () => { - const { driver, pageObjects } = await setupWebDriver({ - props: { - cardActionMiddleware: - ({ dispatch }) => - next => - ({ cardAction, getSignInUrl }) => { - if (cardAction.type === 'signin') { - getSignInUrl().then(url => { - dispatch({ - type: 'WEB_CHAT/SEND_MESSAGE', - payload: { - text: `Signing into ${new URL(url).host}` - } - }); - }); - } else { - return next(cardAction); - } - } - }, - useProductionBot: true - }); - - await driver.wait(uiConnected(), timeouts.directLine); - await pageObjects.sendMessageViaSendBox('oauth', { waitForSend: true }); - - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); - - const transcript = await getTranscript(driver); - const openUrlButton = await transcript.findElement(By.css('.webchat__bubble__content button')); - - await openUrlButton.click(); - await driver.wait(minNumActivitiesShown(4), timeouts.directLine); - await driver.wait(allOutgoingActivitiesSent(), timeouts.directLine); - - // When the "Sign in" button is clicked, the focus move to it, need to blur it. - await driver.executeScript(() => { - const { activeElement } = document; - - activeElement && activeElement.blur(); - }); - - const base64PNG = await driver.takeScreenshot(); - - expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); -}); - -test('card action "signin" when directLine.getSessionId is falsy', async () => { - const { driver, pageObjects } = await setupWebDriver({ - disableNoMagicCode: true, - props: { - cardActionMiddleware: - ({ dispatch }) => - next => - ({ cardAction, getSignInUrl }) => { - if (cardAction.type === 'signin') { - Promise.resolve(getSignInUrl()).then(url => { - dispatch({ - type: 'WEB_CHAT/SEND_MESSAGE', - payload: { - text: `Signing into ${new URL(url).host}` - } - }); - }); - } else { - return next(cardAction); - } - } - }, - useProductionBot: true - }); - - await driver.wait(uiConnected(), timeouts.directLine); - await pageObjects.sendMessageViaSendBox('oauth', { waitForSend: true }); - - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); - - const transcript = await getTranscript(driver); - const openUrlButton = await transcript.findElement(By.css('.webchat__bubble__content button')); - - await openUrlButton.click(); - await driver.wait(minNumActivitiesShown(4), timeouts.directLine); - await driver.wait(allOutgoingActivitiesSent(), timeouts.directLine); - - // When the "Sign in" button is clicked, the focus move to it, need to blur it. - await driver.executeScript(() => { - const { activeElement } = document; - - activeElement && activeElement.blur(); - }); - - const base64PNG = await driver.takeScreenshot(); - - expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); - - await expect(pageObjects.getConsoleErrors()).resolves.toEqual([]); - - expect( - (await pageObjects.getConsoleWarnings()).includes( - 'botframework-webchat: OAuth is not supported on this Direct Line adapter.' - ) - ).toBe(true); -}); diff --git a/__tests__/html/accessibility.adaptiveCard.disabled.inputs.html b/__tests__/html/accessibility.adaptiveCard.disabled.inputs.html index f327db9937..18163e3b72 100644 --- a/__tests__/html/accessibility.adaptiveCard.disabled.inputs.html +++ b/__tests__/html/accessibility.adaptiveCard.disabled.inputs.html @@ -77,7 +77,6 @@ 'DOWN', // What color do you want? Red 'DOWN', // Green 'TAB', - 'TAB', // AC 2.11: Checkbox group container has tabindex="0" 'SPACE', // What colors do you want? Red 'TAB', 'SPACE', // Green diff --git a/__tests__/html/assets/esm/speech/MockedSpeechSynthesis.js b/__tests__/html/assets/esm/speech/MockedSpeechSynthesis.js new file mode 100644 index 0000000000..0efc7e5d8c --- /dev/null +++ b/__tests__/html/assets/esm/speech/MockedSpeechSynthesis.js @@ -0,0 +1,133 @@ +import { EventTargetProperties } from 'https://esm.sh/event-target-properties'; +import SpeechSynthesisEvent from './MockedSpeechSynthesisEvent.js'; +import SpeechSynthesisVoice from './MockedSpeechSynthesisVoice.js'; + +export default class SpeechSynthesis extends EventTarget { + constructor() { + super(); + + this.#eventTargetProperties = new EventTargetProperties(this); + } + + /** @type {SpeechSynthesisUtterance} */ + #currentUtterance; + /** @type {EventTargetProperties} */ + #eventTargetProperties; + /** @type {boolean} */ + #paused = false; + // #pending = false; + /** @type {SpeechSynthesisUtterance[]} */ + #queue = []; + /** @type {boolean} */ + #speaking = false; + + get onvoiceschanged() { + return this.#eventTargetProperties.getProperty('voiceschanged'); + } + + set onvoiceschanged(value) { + this.#eventTargetProperties.setProperty('voiceschanged', value); + } + + /** @type {boolean} */ + get paused() { + return this.#paused; + } + + /** @type {boolean} */ + get pending() { + return !!this.#queue.length; + } + + /** @type {boolean} */ + get speaking() { + return !this.paused && this.#speaking; + } + + cancel() { + this.#paused = false; + this.#speaking = false; + this.#queue.splice(0); + + this.#currentUtterance?.dispatchEvent(new SpeechSynthesisEvent('end', { utterance: this.#currentUtterance })); + } + + getVoices() { + return Object.freeze([ + SpeechSynthesisVoice.from({ + default: true, + lang: 'en-US', + localService: true, + name: 'Mock Voice (en-US)', + voiceURI: 'mock://web-speech/voice/en-US' + }), + SpeechSynthesisVoice.from({ + default: false, + lang: 'zh-YUE', + localService: true, + name: 'Mock Voice (zh-YUE)', + voiceURI: 'mock://web-speech/voice/zh-YUE' + }) + ]); + } + + pause() { + if (this.#paused) { + return; + } + + this.#paused = true; + + this.#currentUtterance?.dispatchEvent(new SpeechSynthesisEvent('pause', { utterance: this.#currentUtterance })); + } + + resume() { + if (!this.#paused) { + return; + } + + this.#paused = false; + + if (this.#currentUtterance) { + this.#currentUtterance.dispatchEvent(new SpeechSynthesisEvent('resume', { utterance: this.#currentUtterance })); + } else { + this.#next(); + } + } + + speak(/** @type {SpeechSynthesisUtterance} */ utterance) { + this.#queue.push(/** @type {SpeechSynthesisUtterance} */ utterance); + + !this.#paused && !this.#speaking && this.#next(); + } + + #next() { + if (this.#paused) { + throw new Error('Should not call #next() when it is paused.'); + } + + this.#currentUtterance = this.#queue.shift(); + + if (!this.#currentUtterance) { + this.#paused = false; + this.#speaking = false; + + return; + } + + this.#speaking = true; + + this.#currentUtterance.addEventListener('end', () => this.#next(), { once: true }); + this.#currentUtterance.addEventListener( + 'error', + () => { + this.#paused = false; + this.#speaking = false; + this.#queue.splice(0); + }, + { once: true } + ); + + this.#currentUtterance.dispatchEvent(new SpeechSynthesisEvent('start', { utterance: this.#currentUtterance })); + } +} diff --git a/__tests__/html/assets/esm/speech/MockedSpeechSynthesisErrorEvent.js b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisErrorEvent.js new file mode 100644 index 0000000000..30c7db7a6e --- /dev/null +++ b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisErrorEvent.js @@ -0,0 +1,21 @@ +import SpeechSynthesisEvent from './MockedSpeechSynthesisEvent.js'; + +export default class SpeechSynthesisErrorEvent extends SpeechSynthesisEvent { + constructor( + /** @type {string} */ + type, + /** @type {EventInitDict} */ + eventInitDict + ) { + super(type, eventInitDict); + + this.#error = eventInitDict.error; + } + + /** @type {unknown} */ + #error; + + get error() { + return this.#error; + } +} diff --git a/__tests__/html/assets/esm/speech/MockedSpeechSynthesisEvent.js b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisEvent.js new file mode 100644 index 0000000000..bd99d28cd0 --- /dev/null +++ b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisEvent.js @@ -0,0 +1,47 @@ +export default class SpeechSynthesisEvent extends Event { + constructor( + /** @type {string} */ + type, + /** @type {EventInitDict} */ + eventInitDict + ) { + super(type, eventInitDict); + + this.#charIndex = eventInitDict.charIndex || 0; + this.#charLength = eventInitDict.charLength || 0; + this.#elapsedTime = eventInitDict.elapsedTime || 0; + this.#name = eventInitDict.name || ''; + this.#utterance = eventInitDict.utterance; + } + + /** @type {number} */ + #charIndex; + /** @type {number} */ + #charLength; + /** @type {number} */ + #elapsedTime; + /** @type {string} */ + #name; + /** @type {SpeechSynthesisUtterance | undefined} */ + #utterance; + + get charIndex() { + return this.#charIndex; + } + + get charLength() { + return this.#charLength; + } + + get elapsedTime() { + return this.#elapsedTime; + } + + get name() { + return this.#name; + } + + get utterance() { + return this.#utterance; + } +} diff --git a/__tests__/html/assets/esm/speech/MockedSpeechSynthesisUtterance.js b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisUtterance.js new file mode 100644 index 0000000000..d8c3bb423d --- /dev/null +++ b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisUtterance.js @@ -0,0 +1,81 @@ +import { EventTargetProperties } from 'https://unpkg.com/event-target-properties@latest/dist/event-target-properties.mjs'; + +export default class SpeechSynthesisUtterance extends EventTarget { + constructor(text) { + super(); + + this.#eventTargetProperties = new EventTargetProperties(this); + this.text = text || ''; + } + + #eventTargetProperties; + + /** @type {string} */ + lang; + /** @type {number} */ + pitch; + /** @type {number} */ + rate; + /** @type {string} */ + text; + /** @type {any} */ + voice; + /** @type {number} */ + volume; + + get onboundary() { + return this.#eventTargetProperties.getProperty('boundary'); + } + + set onboundary(value) { + this.#eventTargetProperties.setProperty('boundary', value); + } + + get onend() { + return this.#eventTargetProperties.getProperty('end'); + } + + set onend(value) { + this.#eventTargetProperties.setProperty('end', value); + } + + get onerror() { + return this.#eventTargetProperties.getProperty('error'); + } + + set onerror(value) { + this.#eventTargetProperties.setProperty('error', value); + } + + get onmark() { + return this.#eventTargetProperties.getProperty('mark'); + } + + set onmark(value) { + this.#eventTargetProperties.setProperty('mark', value); + } + + get onpause() { + return this.#eventTargetProperties.getProperty('pause'); + } + + set onpause(value) { + this.#eventTargetProperties.setProperty('pause', value); + } + + get onresume() { + return this.#eventTargetProperties.getProperty('resume'); + } + + set onresume(value) { + this.#eventTargetProperties.setProperty('resume', value); + } + + get onstart() { + return this.#eventTargetProperties.getProperty('start'); + } + + set onstart(value) { + this.#eventTargetProperties.setProperty('start', value); + } +} diff --git a/__tests__/html/assets/esm/speech/MockedSpeechSynthesisVoice.js b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisVoice.js new file mode 100644 index 0000000000..ce48b722ac --- /dev/null +++ b/__tests__/html/assets/esm/speech/MockedSpeechSynthesisVoice.js @@ -0,0 +1,48 @@ +export default class SpeechSynthesisVoice { + /** @type {boolean} */ + #default = false; + + /** @type {string} */ + #lang = ''; + + /** @type {boolean} */ + #localService = false; + + /** @type {string} */ + #name = ''; + + /** @type {string} */ + #voiceURI = ''; + + get default() { + return this.#default; + } + + get lang() { + return this.#lang; + } + + get localService() { + return this.#localService; + } + + get name() { + return this.#name; + } + + get voiceURI() { + return this.#voiceURI; + } + + static from(init) { + const voice = new SpeechSynthesisVoice(); + + voice.#default = init.default; + voice.#lang = init.lang; + voice.#localService = init.localService; + voice.#name = init.name; + voice.#voiceURI = init.voiceURI; + + return voice; + } +} diff --git a/__tests__/html/assets/esm/speech/speechPageObjects.js b/__tests__/html/assets/esm/speech/speechPageObjects.js new file mode 100644 index 0000000000..e399cd3376 --- /dev/null +++ b/__tests__/html/assets/esm/speech/speechPageObjects.js @@ -0,0 +1,223 @@ +/* eslint-disable no-empty-function */ +/* eslint-env browser */ + +import { + SpeechGrammarList, + SpeechRecognition, + SpeechRecognitionAlternative, + SpeechRecognitionErrorEvent, + SpeechRecognitionEvent, + SpeechRecognitionResult, + SpeechRecognitionResultList +} from 'react-dictate-button/internal'; +import { fn, spyOn } from 'jest-mock'; +import SpeechSynthesis from './MockedSpeechSynthesis.js'; +import SpeechSynthesisErrorEvent from './MockedSpeechSynthesisErrorEvent.js'; +import SpeechSynthesisEvent from './MockedSpeechSynthesisEvent.js'; +import SpeechSynthesisUtterance from './MockedSpeechSynthesisUtterance.js'; +import SpeechSynthesisVoice from './MockedSpeechSynthesisVoice.js'; + +function createWebSpeechPonyfill() { + const speechSynthesis = new SpeechSynthesis(); + + return { + SpeechGrammarList, + SpeechRecognition: fn().mockImplementation(() => { + const speechRecognition = new SpeechRecognition(); + + spyOn(speechRecognition, 'abort'); + spyOn(speechRecognition, 'start'); + + return speechRecognition; + }), + speechSynthesis, + SpeechSynthesisErrorEvent, + SpeechSynthesisEvent, + SpeechSynthesisUtterance, + SpeechSynthesisVoice + }; +} + +/** + * @deprecated + */ +async function actSpeakOnce({ speechSynthesis }, actor, speakActor) { + let lastUtterance; + + spyOn(speechSynthesis, 'speak').mockImplementationOnce(async utterance => { + lastUtterance = utterance; + + utterance.dispatchEvent(new SpeechSynthesisEvent('start', { utterance })); + + await speakActor?.(utterance); + + utterance.dispatchEvent(new SpeechSynthesisEvent('end', { utterance })); + }); + + await actor(); + + return lastUtterance; +} + +async function actSpeak({ speechSynthesis }, actor, speakActor) { + const utterances = []; + + spyOn(speechSynthesis, 'speak').mockImplementation(async utterance => { + utterances.push(utterance); + + utterance.dispatchEvent(new SpeechSynthesisEvent('start', { utterance })); + + try { + await speakActor?.(utterance); + } catch (error) { + utterance.dispatchEvent(new SpeechSynthesisErrorEvent('error', { error, utterance })); + + return; + } + + utterance.dispatchEvent(new SpeechSynthesisEvent('end', { utterance })); + }); + + await actor(); + + return Object.freeze(utterances); +} + +function actRecognizeOnce(ponyfill, actor, recognizeActor) { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async resolve => { + const OriginalSpeechRecognition = ponyfill.SpeechRecognition; + + spyOn(ponyfill, 'SpeechRecognition').mockImplementationOnce(() => { + const speechRecognition = new OriginalSpeechRecognition(); + + let started = false; + let audioStarted = false; + let soundStarted = false; + let speechStarted = false; + + const end = () => { + started && speechRecognition.dispatchEvent(new Event('end')); + started = false; + }; + + const audioEnd = () => { + audioStarted && speechRecognition.dispatchEvent(new Event('audioend')); + audioStarted = false; + }; + + const soundEnd = () => { + soundStarted && speechRecognition.dispatchEvent(new Event('soundend')); + soundStarted = false; + }; + + const speechEnd = () => { + speechStarted && speechRecognition.dispatchEvent(new Event('speechend')); + speechStarted = false; + }; + + const start = () => { + started || speechRecognition.dispatchEvent(new Event('start')); + started = true; + }; + + const audioStart = () => { + audioStarted || speechRecognition.dispatchEvent(new Event('audiostart')); + audioStarted = true; + }; + + const soundStart = () => { + soundStarted || speechRecognition.dispatchEvent(new Event('soundstart')); + soundStarted = true; + }; + + const speechStart = () => { + speechStarted || speechRecognition.dispatchEvent(new Event('speechstart')); + speechStarted = true; + }; + + const error = reason => { + speechRecognition.dispatchEvent(new SpeechRecognitionErrorEvent('error', { error: reason })); + started = true; + }; + + const result = text => { + speechRecognition.dispatchEvent( + new SpeechRecognitionEvent('result', { + results: new SpeechRecognitionResultList( + new SpeechRecognitionResult(new SpeechRecognitionAlternative(1.0, text)) + ) + }) + ); + }; + + const finalizedResult = text => { + speechRecognition.dispatchEvent( + new SpeechRecognitionEvent('result', { + results: new SpeechRecognitionResultList( + SpeechRecognitionResult.fromFinalized(new SpeechRecognitionAlternative(1.0, text)) + ) + }) + ); + }; + + spyOn(speechRecognition, 'abort').mockImplementationOnce(() => { + speechEnd(); + soundEnd(); + audioEnd(); + error('aborted'); + end(); + }); + + spyOn(speechRecognition, 'start').mockImplementationOnce(async () => { + if (typeof recognizeActor === 'string') { + start(); + audioStart(); + soundStart(); + speechStart(); + finalizedResult(recognizeActor); + speechEnd(); + soundEnd(); + audioEnd(); + end(); + } else { + await recognizeActor?.({ + start, + audioStart, + soundStart, + speechStart, + speechEnd, + soundEnd, + audioEnd, + error, + end, + result, + finalizedResult + }); + } + + resolve(); + }); + + return speechRecognition; + }); + + await actor(); + }); +} + +async function sendMessageViaMicrophone(speechPonyfill, text) { + const clickMicrophoneButton = () => host.click(document.querySelector(`.webchat__microphone-button__button`)); + + await actRecognizeOnce( + speechPonyfill, + speechPonyfill.speechSynthesis + ? () => + // WHEN: Microphone button is clicked and synthesized empty utterace for user gesture requirement. + actSpeakOnce(speechPonyfill, clickMicrophoneButton) + : clickMicrophoneButton, + text + ); +} + +export { actRecognizeOnce, actSpeak, actSpeakOnce, createWebSpeechPonyfill, sendMessageViaMicrophone }; diff --git a/__tests__/html/basic/sendBox.trimOutgoing.html b/__tests__/html/basic/sendBox.trimOutgoing.html new file mode 100644 index 0000000000..ebc5112c4a --- /dev/null +++ b/__tests__/html/basic/sendBox.trimOutgoing.html @@ -0,0 +1,55 @@ + + +
+ + + + + + + + + + + + diff --git a/__tests__/html/basic/sendBox.trimOutgoing.js b/__tests__/html/basic/sendBox.trimOutgoing.js new file mode 100644 index 0000000000..f71a4e97d3 --- /dev/null +++ b/__tests__/html/basic/sendBox.trimOutgoing.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('basic/sendBox.trimOutgoing.html', () => runHTML('basic/sendBox.trimOutgoing.html')); diff --git a/__tests__/html/cardAction/openURL.html b/__tests__/html/cardAction/openURL.html new file mode 100644 index 0000000000..285826a394 --- /dev/null +++ b/__tests__/html/cardAction/openURL.html @@ -0,0 +1,52 @@ + + + + + + + + + + + + + diff --git a/__tests__/html/cardAction/openURL.js b/__tests__/html/cardAction/openURL.js new file mode 100644 index 0000000000..5657eea186 --- /dev/null +++ b/__tests__/html/cardAction/openURL.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('cardAction/openURL.html', () => runHTML('cardAction/openURL.html')); diff --git a/__tests__/html/cardAction/signIn.html b/__tests__/html/cardAction/signIn.html new file mode 100644 index 0000000000..c5e3c98985 --- /dev/null +++ b/__tests__/html/cardAction/signIn.html @@ -0,0 +1,60 @@ + + + + + + + + + + + + + diff --git a/__tests__/html/cardAction/signIn.js b/__tests__/html/cardAction/signIn.js new file mode 100644 index 0000000000..ca2a68a432 --- /dev/null +++ b/__tests__/html/cardAction/signIn.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('cardAction/signIn.html', () => runHTML('cardAction/signIn.html')); diff --git a/__tests__/html/cardAction/signIn.noGetSessionId.html b/__tests__/html/cardAction/signIn.noGetSessionId.html new file mode 100644 index 0000000000..ffe5293ead --- /dev/null +++ b/__tests__/html/cardAction/signIn.noGetSessionId.html @@ -0,0 +1,75 @@ + + + + + + + + + + + + + diff --git a/__tests__/html/cardAction/signIn.noGetSessionId.js b/__tests__/html/cardAction/signIn.noGetSessionId.js new file mode 100644 index 0000000000..03fa4d8be9 --- /dev/null +++ b/__tests__/html/cardAction/signIn.noGetSessionId.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('cardAction/signIn.noGetSessionId.html', () => runHTML('cardAction/signIn.noGetSessionId.html')); diff --git a/__tests__/html/chatAdapter.directLineAppServiceExtension.html b/__tests__/html/chatAdapter.directLineAppServiceExtension.html index 3f50ef624a..af4f9cb00d 100644 --- a/__tests__/html/chatAdapter.directLineAppServiceExtension.html +++ b/__tests__/html/chatAdapter.directLineAppServiceExtension.html @@ -1,4 +1,4 @@ - + @@ -10,19 +10,18 @@ + + + + + + + + + diff --git a/__tests__/html/speech/synthesis/adaptiveCards.js b/__tests__/html/speech/synthesis/adaptiveCards.js new file mode 100644 index 0000000000..cd2b0afb05 --- /dev/null +++ b/__tests__/html/speech/synthesis/adaptiveCards.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('speech/synthesis/adaptiveCards.html', () => runHTML('speech/synthesis/adaptiveCards.html')); diff --git a/__tests__/html/speech/synthesis/consecutiveMessages.html b/__tests__/html/speech/synthesis/consecutiveMessages.html new file mode 100644 index 0000000000..3f2276f801 --- /dev/null +++ b/__tests__/html/speech/synthesis/consecutiveMessages.html @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + diff --git a/__tests__/html/speech/synthesis/consecutiveMessages.js b/__tests__/html/speech/synthesis/consecutiveMessages.js new file mode 100644 index 0000000000..44eee4f2ca --- /dev/null +++ b/__tests__/html/speech/synthesis/consecutiveMessages.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('speech/synthesis/consecutiveMessages.html', () => runHTML('speech/synthesis/consecutiveMessages.html')); diff --git a/__tests__/html/speech/synthesis/disableSynthesis.html b/__tests__/html/speech/synthesis/disableSynthesis.html new file mode 100644 index 0000000000..061cf768b4 --- /dev/null +++ b/__tests__/html/speech/synthesis/disableSynthesis.html @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + diff --git a/__tests__/html/speech/synthesis/disableSynthesis.js b/__tests__/html/speech/synthesis/disableSynthesis.js new file mode 100644 index 0000000000..2deb0303b9 --- /dev/null +++ b/__tests__/html/speech/synthesis/disableSynthesis.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('speech/synthesis/disableSynthesis.html', () => runHTML('speech/synthesis/disableSynthesis.html')); diff --git a/__tests__/html/speech/synthesis/startRecognitionAfterFailedSynthesis.html b/__tests__/html/speech/synthesis/startRecognitionAfterFailedSynthesis.html new file mode 100644 index 0000000000..ba0ec26e8d --- /dev/null +++ b/__tests__/html/speech/synthesis/startRecognitionAfterFailedSynthesis.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + diff --git a/__tests__/html/speech/synthesis/startRecognitionAfterFailedSynthesis.js b/__tests__/html/speech/synthesis/startRecognitionAfterFailedSynthesis.js new file mode 100644 index 0000000000..085fde28a2 --- /dev/null +++ b/__tests__/html/speech/synthesis/startRecognitionAfterFailedSynthesis.js @@ -0,0 +1,4 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('speech/synthesis/startRecognitionAfterFailedSynthesis.html', () => + runHTML('speech/synthesis/startRecognitionAfterFailedSynthesis.html')); diff --git a/__tests__/html/speech/synthesis/stopSynthesisOnMicrophoneButton.html b/__tests__/html/speech/synthesis/stopSynthesisOnMicrophoneButton.html new file mode 100644 index 0000000000..0652ee2753 --- /dev/null +++ b/__tests__/html/speech/synthesis/stopSynthesisOnMicrophoneButton.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + diff --git a/__tests__/html/speech/synthesis/stopSynthesisOnMicrophoneButton.js b/__tests__/html/speech/synthesis/stopSynthesisOnMicrophoneButton.js new file mode 100644 index 0000000000..05da214a3e --- /dev/null +++ b/__tests__/html/speech/synthesis/stopSynthesisOnMicrophoneButton.js @@ -0,0 +1,4 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('speech/synthesis/stopSynthesisOnMicrophoneButton.html', () => + runHTML('speech/synthesis/stopSynthesisOnMicrophoneButton.html')); diff --git a/__tests__/html/speechRecognition.simple.js b/__tests__/html/speechRecognition.simple.js index 1361506b36..f14d1c2edd 100644 --- a/__tests__/html/speechRecognition.simple.js +++ b/__tests__/html/speechRecognition.simple.js @@ -25,7 +25,7 @@ describe.each([ { useDirectLineSpeech: true, useHostname: true, useSubscriptionKey: true } ] ])('speech recognition using %s', (_, { useSubscriptionKey, useDirectLineSpeech, useHostname }) => { - test.nightly('should recognize "Hello, World!".', async () => { + test.skip('should recognize "Hello, World!".', async () => { if (!useDirectLineSpeech && !COGNITIVE_SERVICES_SUBSCRIPTION_KEY) { throw new Error('"COGNITIVE_SERVICES_SUBSCRIPTION_KEY" must be set.'); } else if (useDirectLineSpeech && !DIRECT_LINE_SPEECH_SUBSCRIPTION_KEY) { @@ -33,7 +33,7 @@ describe.each([ } const { token } = await ( - await fetch('https://webchat-mockbot3.azurewebsites.net/api/token/directline', { method: 'POST' }) + await fetch('https://hawo-mockbot4-token-app.ambitiousflower-67725bfd.westus.azurecontainerapps.io/api/token/directline', { method: 'POST' }) ).json(); const params = new URLSearchParams({ diff --git a/__tests__/html/video/vimeo.sandbox.html b/__tests__/html/video/vimeo.sandbox.html new file mode 100644 index 0000000000..73d2c59b06 --- /dev/null +++ b/__tests__/html/video/vimeo.sandbox.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + diff --git a/__tests__/html/video/vimeo.sandbox.js b/__tests__/html/video/vimeo.sandbox.js new file mode 100644 index 0000000000..b756788174 --- /dev/null +++ b/__tests__/html/video/vimeo.sandbox.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('video/vimeo.sandbox.html', () => runHTML('video/vimeo.sandbox.html')); diff --git a/__tests__/html/video/youtube.sandbox.html b/__tests__/html/video/youtube.sandbox.html new file mode 100644 index 0000000000..8ed5f9b4d4 --- /dev/null +++ b/__tests__/html/video/youtube.sandbox.html @@ -0,0 +1,44 @@ + + + + + + + + + + + + + diff --git a/__tests__/html/video/youtube.sandbox.js b/__tests__/html/video/youtube.sandbox.js new file mode 100644 index 0000000000..5a6db95141 --- /dev/null +++ b/__tests__/html/video/youtube.sandbox.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test('video/youtube.sandbox.html', () => runHTML('video/youtube.sandbox.html')); diff --git a/__tests__/html/video/youtube.skip.html b/__tests__/html/video/youtube.skip.html new file mode 100644 index 0000000000..557e6f6ded --- /dev/null +++ b/__tests__/html/video/youtube.skip.html @@ -0,0 +1,80 @@ + + + + + + + + + + + diff --git a/__tests__/html/video/youtube.skip.js b/__tests__/html/video/youtube.skip.js new file mode 100644 index 0000000000..cfd0678d42 --- /dev/null +++ b/__tests__/html/video/youtube.skip.js @@ -0,0 +1,3 @@ +/** @jest-environment ./packages/test/harness/src/host/jest/WebDriverEnvironment.js */ + +test.skip('video/youtube.skip.html', () => runHTML('video/youtube.skip.html')); diff --git a/__tests__/sendBox.js b/__tests__/sendBox.js index 744c159b43..98b4b077fd 100644 --- a/__tests__/sendBox.js +++ b/__tests__/sendBox.js @@ -1,6 +1,5 @@ import { imageSnapshotOptions, timeouts } from './constants.json'; -import actionDispatched from './setup/conditions/actionDispatched'; import minNumActivitiesShown from './setup/conditions/minNumActivitiesShown'; import uiConnected from './setup/conditions/uiConnected'; @@ -32,23 +31,3 @@ test('should focus send box when message is being sent', async () => { expect(base64PNG).toMatchImageSnapshot(imageSnapshotOptions); }); - -test('should trim outgoing message when being sent', async () => { - const { driver, pageObjects } = await setupWebDriver(); - - await driver.wait(uiConnected(), timeouts.directLine); - await pageObjects.sendMessageViaSendBox( - '\u00A0\u00A0There should be no space before and after this message.\u00A0\u00A0', - { waitForSend: false } - ); - await driver.wait( - actionDispatched( - ({ payload: { activity } = {}, type }) => - type === 'DIRECT_LINE/INCOMING_ACTIVITY' && - activity.from.role === 'user' && - activity.text === 'There should be no space before and after this message.' - ), - timeouts.directLine - ); - await driver.wait(minNumActivitiesShown(2), timeouts.directLine); -}); diff --git a/__tests__/setup/web/index.html b/__tests__/setup/web/index.html index 23f5fb7ae4..cea691a8e6 100644 --- a/__tests__/setup/web/index.html +++ b/__tests__/setup/web/index.html @@ -1,4 +1,4 @@ - +