Skip to content
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ Legends:
### Added

- Added `styleOptions.richCardTitleOmitHeadingRole` (default `false`) to opt out of `style: 'heading'` on rich card titles, in PR [#5839](https://github.com/microsoft/BotFramework-WebChat/pull/5839), by [@cjennison](https://github.com/cjennison)
- Added support of Adaptive Cards `Action.Submit` action with `msteams/signin` sub-action to open sign-in link in a popup window, in PR [#5860](https://github.com/microsoft/BotFramework-WebChat/pull/5860), by [@compulim](https://github.com/compulim)
- Added `styleOptions.adaptiveCardSignInActionPopupWindowHeight/Width` for sizing the sign-in popup window
- Link to [Adaptive Cards spec](https://adaptivecards.microsoft.com/?topic=SigninSubmitActionData)
- Refer to [this test](./__tests__/html2/adaptiveCard/signInAction.html) for the reference payload
- Note: this implementation is based on observation of how Microsoft Teams behave and could deviate from their official implementation

### Fixed

- Fixed an error when a failed activity is present when Web Chat mounts, resolving [#5812](https://github.com/microsoft/BotFramework-WebChat/issues/5812), in PR [#5848](https://github.com/microsoft/BotFramework-WebChat/pull/5848), by [@OEvgeny](https://github.com/OEvgeny)


## [4.19.1] - 2026-06-09

### Changed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js",
"react": "/__dist__/packages/bundle/static/react.js",
"react-dom": "/__dist__/packages/bundle/static/react-dom.js"
}
}
</script>
<script type="module">
import '/test-harness.mjs';
import '/test-page-object.mjs';

import { createDirectLine, createStoreWithOptions, renderWebChat } from 'botframework-webchat';

testHelpers.hideKnownError();

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

// TODO: This is for `createDirectLineEmulator` only, should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions };

const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity({
attachments: [
{
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
type: 'AdaptiveCard',
version: '1.2',
body: [
{
text: 'Hello, World!',
type: 'TextBlock'
},
{
actions: [
{
title: 'Sign in',
type: 'Action.Submit',
data: {
msteams: {
type: 'signin',
value: 'mailto:johndoe@microsoft.com'
}
}
}
],
type: 'ActionSet'
}
]
}
}
],
from: {
id: 'bot',
role: 'bot'
},
id: '1',
timestamp: new Date().toISOString(),
type: 'message'
});

// THEN: Should render the Adaptive Card with a "Sign in" button.
await pageConditions.numActivitiesShown(1);

// GIVEN: Intercept `console.warn`.
const originalConsoleWarn = console.warn.bind(console);
let consoleWarnArgs;

console.warn = (...args) => {
consoleWarnArgs = args;

return originalConsoleWarn(...args);
};

// GIVEN: Intercept `window.open`.
const originalOpen = window.open.bind(window);
let windowOpenCalled = false;

window.open = (...args) => {
windowOpenCalled = true;

return originalOpen(...args);
};

// WHEN: The "Sign in" button is clicked.
await host.click(document.querySelector('[aria-label="Sign in"]'));

// THEN: Should not call `window.open`.
expect(windowOpenCalled).toBe(false);

// THEN: Should warn.
expect(consoleWarnArgs).toEqual([
'botframework-webchat: "Action.Submit/msteams" sub-action validation error.',
'"value" must have protocol of either "http:" or "https:"'
]);
});
</script>
</body>
</html>
126 changes: 126 additions & 0 deletions __tests__/html2/adaptiveCard/msteams/signInSubAction.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js",
"react": "/__dist__/packages/bundle/static/react.js",
"react-dom": "/__dist__/packages/bundle/static/react-dom.js"
}
}
</script>
<script type="module">
import '/test-harness.mjs';
import '/test-page-object.mjs';

import { createDirectLine, createStoreWithOptions, renderWebChat } from 'botframework-webchat';

testHelpers.hideKnownError();

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

// TODO: This is for `createDirectLineEmulator` only, should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions };

const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity({
attachments: [
{
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
type: 'AdaptiveCard',
version: '1.2',
body: [
{
text: 'Hello, World!',
type: 'TextBlock'
},
{
actions: [
{
title: 'Sign in',
type: 'Action.Submit',
data: {
msteams: {
type: 'signin',
value: new URL('signInSubAction.skip.html', location).toString()
}
}
}
],
type: 'ActionSet'
}
]
}
}
],
from: {
id: 'bot',
role: 'bot'
},
id: '1',
timestamp: new Date().toISOString(),
type: 'message'
});

// THEN: Should render the Adaptive Card with a "Sign in" button.
await pageConditions.numActivitiesShown(1);
await host.snapshot('local');

// GIVEN: Intercept window.open to watch the popup window to be closed.
const originalOpen = window.open.bind(window);
const popupClosedDeferred = Promise.withResolvers();
let windowOpenArgs;

window.open = (...args) => {
const popup = originalOpen(...args);

windowOpenArgs = args;

// Note: popup.addEventListener('close') does not dispatch.
const interval = setInterval(() => {
if (popup.closed) {
clearInterval(interval);

popupClosedDeferred.resolve();
}
}, 100);

return popup;
};

// WHEN: The "Sign in" button is clicked.
await host.click(document.querySelector('[aria-label="Sign in"]'));

// THEN: Popup window should close itself.
await Promise.race([
popupClosedDeferred.promise,
// THEN: Fail if popup window does not close after 2 seconds.
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timed out waiting for popup window to be closed')), 2_000)
)
]);

expect(windowOpenArgs).toEqual([
expect.stringContaining('signInSubAction.skip.html'),
'_blank',
'height=640,popup,width=480'
]);
});
</script>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<!doctype html>
<html lang="en-US">
<head>
<link href="/assets/index.css" rel="stylesheet" type="text/css" />
</head>
<body>
<main id="webchat"></main>
<script type="importmap">
{
"imports": {
"botframework-webchat": "/__dist__/packages/bundle/static/botframework-webchat.js",
"react": "/__dist__/packages/bundle/static/react.js",
"react-dom": "/__dist__/packages/bundle/static/react-dom.js"
}
}
</script>
<script type="module">
import '/test-harness.mjs';
import '/test-page-object.mjs';

import { createDirectLine, createStoreWithOptions, renderWebChat } from 'botframework-webchat';

testHelpers.hideKnownError();

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

// TODO: This is for `createDirectLineEmulator` only, should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions };

const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity({
attachments: [
{
contentType: 'application/vnd.microsoft.card.adaptive',
content: {
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
type: 'AdaptiveCard',
version: '1.2',
body: [
{
text: 'Hello, World!',
type: 'TextBlock'
},
{
actions: [
{
title: 'Sign in',
type: 'Action.Submit',
data: {
msteams: {
type: 'signin',
value: '/index.html'
}
}
}
],
type: 'ActionSet'
}
]
}
}
],
from: {
id: 'bot',
role: 'bot'
},
id: '1',
timestamp: new Date().toISOString(),
type: 'message'
});

// THEN: Should render the Adaptive Card with a "Sign in" button.
await pageConditions.numActivitiesShown(1);

// GIVEN: Intercept `console.warn`.
const originalConsoleWarn = console.warn.bind(console);
let consoleWarnArgs;

console.warn = (...args) => {
consoleWarnArgs = args;

return originalConsoleWarn(...args);
};

// GIVEN: Intercept `window.open`.
const originalOpen = window.open.bind(window);
let windowOpenCalled = false;

window.open = (...args) => {
windowOpenCalled = true;

return originalOpen(...args);
};

// WHEN: The "Sign in" button is clicked.
await host.click(document.querySelector('[aria-label="Sign in"]'));

// THEN: Should not call `window.open`.
expect(windowOpenCalled).toBe(false);

// THEN: Should warn.
expect(consoleWarnArgs).toEqual([
'botframework-webchat: "Action.Submit/msteams" sub-action validation error.',
'"value" must be an absolute URL',
'"value" must have protocol of either "http:" or "https:"'
]);
});
</script>
</body>
</html>
Loading
Loading