Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
6893734
Initial commit
compulim Dec 20, 2025
0a0bc30
Refactor into files
compulim Dec 20, 2025
e385449
Split internal and external native API
compulim Dec 20, 2025
1a65da4
Rename
compulim Dec 20, 2025
6fbecc0
Remove ESLint
compulim Dec 20, 2025
2546022
Simple hideKnownError
compulim Dec 20, 2025
ff827a1
Simplify breakpoint and add debugger()
compulim Dec 20, 2025
adf4691
Make debugger a property
compulim Dec 20, 2025
52db426
Refactor to Debug API
compulim Dec 20, 2025
537d137
Remove obsoleted test
compulim Dec 20, 2025
a8b96f5
Rename to debug API
compulim Dec 20, 2025
4976f86
Add entry
compulim Dec 20, 2025
83c64df
Add debug API to ActivityRow
compulim Dec 21, 2025
ed7de71
Simplify debug API
compulim Dec 21, 2025
25be5f0
Add playground
compulim Dec 21, 2025
33cc6f8
Merge branch 'main' into feat-debug
compulim Dec 21, 2025
1e85922
Allow arguments
compulim Dec 21, 2025
6195a37
Fix Prettier
compulim Dec 21, 2025
78c9c18
Fix precommit
compulim Dec 21, 2025
f8a110f
Add tests for lockdown
compulim Dec 21, 2025
8f26856
Remove comment
compulim Dec 21, 2025
f47dd84
Remove export of RootDebugAPI type
compulim Dec 21, 2025
9bfb4f2
Remove no use before define rule
compulim Dec 21, 2025
4c51d4b
Add doc
compulim Dec 21, 2025
8006d45
Add footnote
compulim Dec 21, 2025
e17d83e
Clean up
compulim Dec 21, 2025
efc8556
Clean up
compulim Dec 21, 2025
45a1b24
Clean up public debug API
compulim Dec 21, 2025
b0cbdde
Secure breakpoint names
compulim Dec 21, 2025
dd898b8
Fix types
compulim Dec 21, 2025
7dfdd58
Fix types
compulim Dec 21, 2025
3126106
Fix types
compulim Dec 21, 2025
5b060ff
Update screenshot
compulim Dec 21, 2025
b1d63ec
Update screenshot
compulim Dec 21, 2025
97e62a7
Fix flakiness
compulim Dec 21, 2025
41c6e53
Link to DEBUGGING.md
compulim Dec 21, 2025
54bd01e
Fix path
compulim Dec 21, 2025
f15b09d
Typo
compulim Dec 21, 2025
643f978
Update docs/DEBUGGING.md
compulim Dec 22, 2025
8124cdd
Use microtask
compulim Dec 22, 2025
84964b5
Use Object.create(null)
compulim Dec 22, 2025
82f36fb
Softer tone
compulim Dec 22, 2025
56d5814
Remove unused Infer* types
compulim Dec 22, 2025
0ab2194
Rename privateDebugAPI to restrictedDebugAPI
compulim Dec 22, 2025
cf6592e
Rename
compulim Dec 22, 2025
171f112
Rename
compulim Dec 23, 2025
767313e
Remove factory function, use class constructor directly
compulim Dec 23, 2025
052e7e9
Add Object.freeze()
compulim Dec 23, 2025
dd4c8c5
Rename to StoreDebugAPI, remove React context, use Registry WeakMap
compulim Dec 23, 2025
3bed1c9
Fix test
compulim Dec 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ Breaking changes in this release:
- Added loading animation for `copilot`, and `fluent` variants
- New JSON-LD graph backend, by [@compulim](https://github.com/compulim) in PR [#5622](https://github.com/microsoft/BotFramework-WebChat/pull/5622)
- Cleanup, by [@compulim](https://github.com/compulim) in PR [#5657](https://github.com/microsoft/BotFramework-WebChat/pull/5657)
- New debug API, by [@compulim](https://github.com/compulim) in PR [#5663](https://github.com/microsoft/BotFramework-WebChat/pull/5663), see [`DEBUGGING.md`](docs/DEBUGGING.md) for more
- Debug into element: open <kbd>F12</kbd>, select the subject in Element pane, type `$0.webChat.debugger`
- Breakpoint: open <kbd>F12</kbd>, select the subject in Element pane, type `$0.webChat.breakpoint.incomingActivity`

### Changed

Expand Down
99 changes: 99 additions & 0 deletions __tests__/html2/debugAPI/breakpoint/activityRow.render.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!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 { createStoreWithOptions, renderWebChat, testIds } from 'botframework-webchat';
import { fn, spyOn } from 'https://esm.sh/jest-mock';
import { waitFor } from 'https://esm.sh/@testduet/wait-for';

const {
testHelpers: { createDirectLineEmulator }
} = window;

testHelpers.hideKnownError();

// TODO: Should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions, testIds };

run(async function () {
const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity({
id: 'a-00001',
text: 'Hello, World!',
type: 'message'
});

await pageConditions.numActivitiesShown(1);

const [activityRowElement] = pageElements.activities();

// THEN: Make sure we are exposing the public version.
expect('breakpoint' in activityRowElement.webChat).toBe(true);
expect('debugger' in activityRowElement.webChat).toBe(true);

// THEN: Make sure we are not exposing the private version.
expect('~types' in activityRowElement.webChat).toBe(false);
expect('toPublic' in activityRowElement.webChat).toBe(false);
expect('UNSAFE_callBreakpoint' in activityRowElement.webChat).toBe(false);

console.log(activityRowElement.webChat);

spyOn(activityRowElement.webChat.breakpoint, 'render');

// WHEN: Activity is updated to "Aloha!"
await directLine.emulateIncomingActivity({
from: {
id: 'bot',
role: 'bot'
},
id: 'a-00001',
text: 'Aloha!',
type: 'message'
});

expect(activityRowElement.webChat.breakpoint.render).toHaveBeenCalledTimes(1);

expect(activityRowElement.webChat.breakpoint.render).toHaveBeenNthCalledWith(1, {
activity: {
from: {
id: 'bot',
role: 'bot'
},
id: expect.any(String),
text: 'Aloha!',
timestamp: expect.any(String),
type: 'message',
channelData: {
'webchat:internal:received-at': expect.any(Number),
'webchat:internal:local-id': expect.any(String),
'webchat:internal:position': 1000
}
}
});

activityRowElement.webChat.breakpoint.render.mockRestore();
});
</script>
</body>
</html>
102 changes: 102 additions & 0 deletions __tests__/html2/debugAPI/breakpoint/root.incomingActivity.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<!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 { createStoreWithOptions, renderWebChat, testIds } from 'botframework-webchat';
import { fn, spyOn } from 'https://esm.sh/jest-mock';
import { waitFor } from 'https://esm.sh/@testduet/wait-for';

const {
testHelpers: { createDirectLineEmulator }
} = window;

testHelpers.hideKnownError();

// TODO: Should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions, testIds };

run(async function () {
const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

const rootElement = document.querySelector('#webchat > *');

// THEN: Make sure we are exposing the public version.
expect('breakpoint' in rootElement.webChat).toBe(true);
expect('debugger' in rootElement.webChat).toBe(true);

// THEN: Make sure we are not exposing the private version.
expect('~types' in rootElement.webChat).toBe(false);
expect('toPublic' in rootElement.webChat).toBe(false);
expect('UNSAFE_callBreakpoint' in rootElement.webChat).toBe(false);
expect('UNSAFE_extendsDebugContextOnce' in rootElement.webChat).toBe(false);

spyOn(rootElement.webChat.breakpoint, 'incomingActivity');

await directLine.emulateIncomingActivity('Hello, World!');

expect(rootElement.webChat.breakpoint.incomingActivity).toHaveBeenCalledTimes(1);

expect(rootElement.webChat.breakpoint.incomingActivity).toHaveBeenNthCalledWith(
1,
{
activities: [
{
channelData: {
'webchat:internal:received-at': expect.any(Number),
'webchat:internal:local-id': expect.any(String),
'webchat:internal:position': 1000
},
from: {
id: 'bot',
role: 'bot'
},
id: expect.any(String),
text: 'Hello, World!',
timestamp: expect.any(String),
type: 'message'
}
]
},
{
activity: {
channelData: {
'webchat:internal:received-at': expect.any(Number),
'webchat:internal:local-id': expect.any(String)
},
from: {
id: 'bot',
role: 'bot'
},
id: expect.any(String),
text: 'Hello, World!',
timestamp: expect.any(String),
type: 'message'
}
}
);

rootElement.webChat.breakpoint.incomingActivity.mockRestore();
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!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 { createStoreWithOptions, renderWebChat, testIds } from 'botframework-webchat';
import { fn, spyOn } from 'https://esm.sh/jest-mock';
import { waitFor } from 'https://esm.sh/@testduet/wait-for';

const {
testHelpers: { createDirectLineEmulator }
} = window;

testHelpers.hideKnownError();

// TODO: Should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions, testIds };

run(async function () {
const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

await directLine.emulateIncomingActivity({
attachments: [
{
content: {
type: 'AdaptiveCard',
body: [
{
type: 'TextBlock',
text: 'You can choose one of the followings.'
}
],
$schema: 'http://adaptivecards.io/schemas/adaptive-card.json',
version: '1.2',
actions: [
{
type: 'Action.Submit',
title: 'What time is it?'
},
{
type: 'Action.Submit',
title: 'What is the weather?'
}
]
},
contentType: 'application/vnd.microsoft.card.adaptive'
}
],
from: { id: 'bot', role: 'bot' },
text: 'What can I do for you?',
type: 'message'
});

await pageConditions.numActivitiesShown(1);
});
</script>
</body>
</html>
79 changes: 79 additions & 0 deletions __tests__/html2/debugAPI/playground/lockdown.skip.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!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 { createStoreWithOptions, renderWebChat, testIds } from 'botframework-webchat';
import { fn, spyOn } from 'https://esm.sh/jest-mock';
import { waitFor } from 'https://esm.sh/@testduet/wait-for';

const {
testHelpers: { createDirectLineEmulator }
} = window;

testHelpers.hideKnownError();

// TODO: Should find ways to eliminate this line.
window.WebChat = { createStoreWithOptions, testIds };

run(async function () {
const { directLine, store } = createDirectLineEmulator();

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

await pageConditions.uiConnected();

const rootElement = document.querySelector('#webchat > *');

const originalDebugger = rootElement.webChat.debugger;
const originalIncomingActivity = rootElement.webChat.breakpoint.incomingActivity;

// Scenario 1.
// If locked down, should fail with:
// "Cannot assign to read only property 'incomingActivity' of object '#<Object>'"
const scenario1 = () => spyOn(rootElement.webChat.breakpoint, 'incomingActivity');

expect(scenario1).toThrow("Cannot assign to read only property 'incomingActivity' of object '[object Object]'");
expect(rootElement.webChat.breakpoint.incomingActivity).toBe(originalIncomingActivity);

// Scenario 2.
// If locked down, the breakpoint object should be frozen.
// There are not errors thrown, the value is kept the same.
const scenario2 = () => {
rootElement.webChat = { breakpoint: { incomingActivity: 123 } };
};

expect(typeof rootElement.webChat.breakpoint.incomingActivity).not.toBe(123);
expect(rootElement.webChat.breakpoint.incomingActivity).toBe(originalIncomingActivity);

// Scenario 3.
// If locked down, the debugger object should be frozen.
const scenario3 = () => {
Object.defineProperty(rootElement.webChat, 'debugger', {
get() {
return 123;
}
});
};

expect(scenario3).toThrow('Cannot define property debugger, object is not extensible');
expect(rootElement.webChat.debugger).toBe(originalDebugger);
});
</script>
</body>
</html>
1 change: 1 addition & 0 deletions __tests__/html2/hooks/useSendFiles.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
sendFiles([blob1, blob2]);

await pageConditions.numActivitiesShown(2);
await pageConditions.allOutgoingActivitiesSent();

await host.snapshot('local');
},
Expand Down
1 change: 1 addition & 0 deletions __tests__/html2/hooks/useSendMessage.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
sendMessage('Hello, World!');

await pageConditions.numActivitiesShown(2);
await pageConditions.allOutgoingActivitiesSent();

await host.snapshot('local');
},
Expand Down
1 change: 1 addition & 0 deletions __tests__/html2/hooks/useSendMessageBack.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
sendMessageBack({ hello: 'World!' }, 'Aloha!', 'Display text');

await pageConditions.numActivitiesShown(2);
await pageConditions.allOutgoingActivitiesSent();

await host.snapshot('local');
},
Expand Down
Loading
Loading