handler.test.ts configuration


Introduction to the handler.test.ts file

This file will contain the unit tests that ensure the operation is working as expected.

This page shows how to create and run tests.

You can directly jump to the examples of positive and negative testing if you are familiar with typescript.

Creating Tests

Tests are configured using configureHandlerTest function of the OperationHandlerTestSetup inteface.

configureHandlerTest takes two arguements:

Parameter Notes
handler Reference to handler function
handlerTest callback Function for configuring and validating the result

handlerTest callback uses given-when-then style of testing.

The inputs are configured in the when block and expect is used to validate the operation result in the then block.

Here is a test for an operation that converts HTML to Markdown:

Copy
Copied
import { OperationHandlerTestSetup } from "@trayio/cdk-dsl/connector/operation/OperationHandlerTest";
import { htmlToMarkdownHandler } from "./handler";
import {
  OperationHandlerError,
  OperationHandlerResult,
} from "@trayio/cdk-dsl/connector/operation/OperationHandler";
import "@trayio/cdk-runtime/connector/operation/OperationHandlerTestRunner";

OperationHandlerTestSetup.configureHandlerTest(
  htmlToMarkdownHandler,
  (handlerTest) =>
    handlerTest
      .usingHandlerContext("test")
      .nothingBeforeAll()
      .testCase("should do something", (testCase) =>
        testCase
          .givenNothing()
          // input object { htmlString: `<h3>Hello world!</h3>` } passed through when block
          .when(() => ({ htmlString: `<h3>Hello world!</h3>` }))
          .then(({ output }) => {
            // use getSuccessfulValueOrFail to get the operation result
            const outputValue =
              OperationHandlerResult.getSuccessfulValueOrFail(output);
            // using expect to validate the result
            expect(outputValue.markdownString).toEqual("### Hello world!");
          })
          .finallyDoNothing()
      )
      .nothingAfterAll()
);

Running Tests

info

You can also use npm test script to run all tests at once.

You can run:

tray-cdk connector test

to test all the operations

OR

tray-cdk connector test [OPERATION_NAME]

to test an individual operation.

Examples

Positive test

You must use getSuccessfulValueOrFail method of OperationHandlerResult object to process the success response from the operation.

Once you have the response you can use jest's expect function to validate the result.

Here is a positive test case for an operation that converts Markdown to HTML:

Copy
Copied
.testCase("should return HTML", (testCase) =>
    testCase
        .givenNothing()
        .when(() => ({ markdownString: `### Hello world!` }))
        .then(({ output }) => {
            const outputValue =
                OperationHandlerResult.getSuccessfulValueOrFail(output);
            expect(outputValue.htmlString).toEqual("<h3>Hello world!</h3>\n");
        })
        .finallyDoNothing()
)

Negative test

For a negative test, the operation (defined in handler.ts) should return a failure. Check error handling section on how to return failures.

Here is an example of a composite operation that converts Contentful's Richtext document to HTML and resolves with an API error upon failure.

handler.tshandler.test.ts
Copy
Copied
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";

export const richtextToHtmlHandler = OperationHandlerSetup.configureHandler<
  DataFormatHelperAuth,
  RichtextToHtmlInput,
  RichtextToHtmlOutput
>((handler) =>
  handler.usingComposite(async (ctx, { richTextDocument }, invoke) => {
    const htmlString = documentToHtmlString(richTextDocument);
    if (htmlString === "")
      return OperationHandlerResult.failure(
        OperationHandlerError.userInputError("Invalid Richtext document")
      );
    return OperationHandlerResult.success({ htmlString });
  })
);
Copy
Copied
.testCase("should fail", (testCase) =>
    testCase
        .givenNothing()
        .when(() => ({ richTextDocument: badRichTextDocument }))
        .then(({ output }) => {
            expect(output.isFailure).toBe(true);
            if (output.isFailure) {
                expect(output.error._tag).toEqual("UserInputError");
                expect(output.error.message).toEqual("Invalid Richtext document");
            }
        })
        .finallyDoNothing()
)

Multiple tests

To ensure your connectors don't return unexpected results, you should write tests for each scenario.

To achieve this, You can chain testCase functions as shown below:

Copy
Copied
.testCase("should return H3", (testCase) =>
    testCase
        .givenNothing()
        .when(() => ({ markdownString: `### Hello world!` }))
        .then(({ output }) => {
            const outputValue =
              OperationHandlerResult.getSuccessfulValueOrFail(output);
            expect(outputValue.htmlString).toEqual("<h3>Hello world!</h3>\n");
        })
        .finallyDoNothing()
).testCase("should return H4", (testCase) =>
    testCase
        .givenNothing()
        .when(() => ({ markdownString: `#### Hello world!` }))
        .then(({ output }) => {
            const outputValue =
              OperationHandlerResult.getSuccessfulValueOrFail(output);
            expect(outputValue.htmlString).toEqual("<h4>Hello world!</h4>\n");
        })
        .finallyDoNothing()
)