Skip to content

Commit

Permalink
fix: [botbuilder-core] TestAdapter throws unhandled promise rejection…
Browse files Browse the repository at this point in the history
… on error (#3990)

* [botbuilder-core] TestAdapter unhandled promises

Description

TestAdapter throws unhalded promise rejections on errors

- The testadapter should handle errors
- For that, TestFlow must implement the thenable implementation correctly

Specific Changes

  - Implemented further the tenable on TestFlow to allow handling catch clauses correctly

Testing

A test is added to verify the behavior change (If you run that test on main before this PR is merged, it fails)

* Updated api

- Ran yarn test:compat --local
  • Loading branch information
alexrecuenco authored Mar 9, 2022
1 parent 277d55b commit 6f3c511
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 3 deletions.
3 changes: 2 additions & 1 deletion libraries/botbuilder-core/etc/botbuilder-core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -810,13 +810,14 @@ export class TestFlow {
assertReplyOneOf(candidates: string[], description?: string, timeout?: number): TestFlow;
catch(onRejected?: (reason: any) => void): TestFlow;
delay(ms: number): TestFlow;
finally(onFinally: () => void): Promise<void>;
// (undocumented)
previous: Promise<void>;
send(userSays: string | Partial<Activity>): TestFlow;
sendConversationUpdate(): TestFlow;
startTest(): Promise<void>;
test(userSays: string | Partial<Activity>, expected: string | Partial<Activity> | ((activity: Partial<Activity>, description?: string) => void), description?: string, timeout?: number): TestFlow;
then(onFulfilled?: () => void): TestFlow;
then(onFulfilled?: () => void, onRejected?: (err: any) => void): TestFlow;
}

// @public (undocumented)
Expand Down
13 changes: 11 additions & 2 deletions libraries/botbuilder-core/src/testAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -978,8 +978,17 @@ export class TestFlow {
* Adds a `then()` step to the tests promise chain.
* @param onFulfilled Code to run if the test is currently passing.
*/
public then(onFulfilled?: () => void): TestFlow {
return new TestFlow(this.previous.then(onFulfilled), this.adapter, this.callback);
public then(onFulfilled?: () => void, onRejected?: (err) => void): TestFlow {
return new TestFlow(this.previous.then(onFulfilled, onRejected), this.adapter, this.callback);
}

/**
* Adds a finally clause. Note that you can't keep chaining afterwards.
* @param onFinally
* @returns
*/
public finally(onFinally: () => void): Promise<void> {
return Promise.resolve(this.previous.finally(onFinally));
}

/**
Expand Down
35 changes: 35 additions & 0 deletions libraries/botbuilder-core/tests/testAdapter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,41 @@ describe(`TestAdapter`, function () {
await adapter.receiveActivity('test');
});

it(`should reject on unhandled error.`, async function () {
const err = new Error('Intentional');
const adapter = new TestAdapter(() => {
throw err;
});
await assert.rejects(async () => {
await adapter.send('Message');
}, err);
});

it(`should call finally when no error happens`, async function () {
const adapter = new TestAdapter(async () => undefined);
let runFinally = false;

await adapter.send('Message').finally(() => {
runFinally = true;
});
assert(runFinally, 'Finally was not called');
});

it(`should call finally when an error happens`, async function () {
const err = new Error('Intentional');
const adapter = new TestAdapter(() => {
throw err;
});
let runFinally = false;

await assert.rejects(async () => {
await adapter.send('Message').finally(() => {
runFinally = true;
});
}, err);
assert(runFinally, 'Finally was not called');
});

it(`should support receiveActivity() called with an Activity.`, async function () {
const adapter = new TestAdapter((context) => {
assert(context.activity.type === ActivityTypes.Message, `wrong type.`);
Expand Down

0 comments on commit 6f3c511

Please sign in to comment.