npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@telefonica/la-bot-testing

v2.0.1

Published

Living Apps Bot Testing Library

Downloads

20

Readme

la-bot-sdk

Testing Library for Living Apps bots

1. Usage

1.1. Install

Set up the dependencies in package.json as follows:

"devDependencies": {
    "@telefonica/la-bot-testing": "^1.0.0-alpha.1",
    "botbuilder-testing": "~4.6.2"
}

Now run npm install.

1.2. Initialization

The Living App testing library needs to be initialized prior to any test launch. The best way to do this is with a beforeEach hook where the beginTesting function is called. This function takes as parameters:

  • an object with partial LivingApp info, at least having the livingApp name and library name, which are required to correctly initialize the dialogs
  • an optional user object, in case you want to configure specific user data
import * as sdk from '@telefonica/la-bot-testing';

beforeEach(() => {
    sdk.beginTesting({ name: 'your-living-app', libraryName: 'your-living-app' });
});

This will allow your tests to be default-initialized and ready for normal dialog flow.

1.3 Normal Testing

import { Channel } from '@telefonica/la-bot-testing';
import * as sdk from '@telefonica/la-bot-testing';
import { DialogTestClient } from 'botbuilder-testing';

import MyDialog from '../../src/dialogs/dialog';
import { ChoiceOperation, RedirectIntent, Screen, SessionData } from '../../src/models';

describe('MyDialog dialog', () => {
    test('MyDialog - no entity & no persistence', async (done) => {
        // Load the dialog with env vars from settings/.env; This can be overrided
        // with specific config entries:
        // { ...sdk.loadConfig('settings/.env), MY_LIVING_APP_CONFIG_ENTRY: "mock://host" }
        const dialog = new MyDialog(sdk.loadConfig('settings/.env'));
        const client = new DialogTestClient('test', dialog, {});
        let messages;

        // We set the intent and entities (if any) that should lead to this dialog
        sdk.setIntent({
            intent: RedirectIntent.HOME,
            entities: [],
        });

        // We set the active channel, the one that would have initialized the request
        sdk.setActiveChannel(Channel.STBH);
        await client.sendActivity('');

        // The dialog has been executed until the first prompt, we can process the
        // messages per channel and assert anything we want
        messages = sdk.getChannelMessages(Channel.STBH);
        expect(messages).toHaveLength(1);
        messages = sdk.getChannelMessages(Channel.MH);
        expect(messages).toHaveLength(1);

        // We can do the same with the SessionData contents
        const sessionData = sdk.getSessionData() as SessionData;
        expect(sessionData.currentOffer).toBe(0);
        expect(sessionData.origin).toBeUndefined();

        // And with the cross-session persistence too
        const persistence = sdk.getPersistenceData();
        expect(persistence.origin).toBeUndefined();

        done();
    });
}

1.4 Advanced Features

There are several advanced features available in case normal flow is not enough.

Channel Configuration

  • function setActiveChannel(channel: Channel): void;
    • As we have seen in the normal example, this sets the requesting channel
  • function resetChannels(): void;
    • This clears the channels from the session, so there are no active channels joined
  • function joinChannel(channel: Channel, state: State = State.STABLE): void;
    • This sets an active channel in one of three possible states: JOINING (useful when testing a start dialog), JOINED (useful when testing what happens when a channel has just joined), STABLE (useful when you do not want to test joining of channels). Remember that a JOINED channel only sends the first message after joining to itself, since this is the joining dispatcher behaviour.
  • function removeChannel(channel: Channel): void;
    • Removes a specific channel from the session.
  • function getChannelActivities(name: Channel): Partial[];
    • Allows you to get the full activities instead of the la-data attachment

Logging configuration

  • function setLoggingOutput(state: boolean): void;
    • Activates logging output to console (default offline)
  • function getBotLogs(): string[];
    • Gives you any log generated by the dialog

Session handling

  • function getSession(): DeepPartial;
    • This will allow you to modify the session object, which usually is not a good idea.

Persistence and SessionData reset

  • function clearSessionData(): void;
  • function clearPersistenceData(): void

Example: Start Dialog testing

import { Channel, State } from '@telefonica/la-bot-testing';
import * as sdk from '@telefonica/la-bot-testing';
import { DialogTestClient } from 'botbuilder-testing';

import MyLivingAppStartDialog from '../../src/dialogs/dialog-la-start';

beforeEach(() => {
    sdk.beginTesting({ name: 'your-living-app', libraryName: 'your-living-app' });
});

describe('Start dialogs', () => {
    test('stbh => mh', async (done) => {
        const dialog = new MyLivingAppStartDialog({});
        const client = new DialogTestClient('test', dialog, {});
        let messages, activity;

        sdk.resetChannels();
        sdk.setIntent({
            intent: 'intent.internal.living-app.start',
            entities: [{ type: 'ent.living_app_name', entity: 'your-living-app' }],
        });

        // First channel to join will be STBH
        sdk.setActiveChannel(Channel.STBH);
        sdk.joinChannel(Channel.STBH, State.JOINING);
        await client.sendActivity('');

        // Only STBH should have message
        messages = sdk.getChannelMessages(Channel.STBH);
        expect(messages).toHaveLength(1);
        expect(messages[0]).toHaveProperty('activeChannels', [Channel.STBH]);
        messages = sdk.getChannelMessages(Channel.MH);
        expect(messages).toHaveLength(0);

        // Only STBH should have activity and no text/html attachment
        activity = sdk.getChannelActivities(Channel.STBH)[0];
        expect(activity.attachments.find((att) => att.contentType === 'text/html')).toBeUndefined();
        activity = sdk.getChannelActivities(Channel.MH)[0];
        expect(activity).toBeUndefined();

        // We clear the messages so we don't have complexity when asserting them
        sdk.clearMessages();

        // Now MH joins the session
        sdk.setActiveChannel(Channel.MH);
        sdk.joinChannel(Channel.MH, State.JOINING);
        await client.sendActivity('');

        // Only MH should have message
        messages = sdk.getChannelMessages(Channel.MH);
        expect(messages).toHaveLength(1);
        expect(messages[0]).toHaveProperty('activeChannels', [Channel.STBH, Channel.MH]);
        messages = sdk.getChannelMessages(Channel.STBH);
        expect(messages).toHaveLength(0);

        // Only MH shoudl have activity, but with no text/html attachment, since STBH is already joined
        activity = sdk.getChannelActivities(Channel.MH)[0];
        expect(activity.attachments.find((att) => att.contentType === 'text/html')).toBeUndefined();
        activity = sdk.getChannelActivities(Channel.STBH)[0];
        expect(activity).toBeUndefined();

        done();
    });

    test('mh => stbh', async (done) => {
        const dialog = new MyLivingAppStartDialog({});
        const client = new DialogTestClient('test', dialog, {});
        let messages, activity;

        sdk.resetChannels();
        sdk.setIntent({
            intent: 'intent.internal.living-app.start',
            entities: [{ type: 'ent.living_app_name', entity: 'your-living-app' }],
        });

        sdk.setActiveChannel(Channel.MH);
        sdk.joinChannel(Channel.MH, State.JOINING);
        await client.sendActivity('');

        messages = sdk.getChannelMessages(Channel.MH);
        expect(messages).toHaveLength(1);
        expect(messages[0]).toHaveProperty('activeChannels', [Channel.MH]);
        messages = sdk.getChannelMessages(Channel.STBH);
        expect(messages).toHaveLength(0);

        // In this case, since MH joins first, the text/html attachment IS defined
        activity = sdk.getChannelActivities(Channel.MH)[0];
        expect(activity.attachments.find((att) => att.contentType === 'text/html')).toHaveProperty('contentUrl');
        activity = sdk.getChannelActivities(Channel.STBH)[0];
        expect(activity).toBeUndefined();

        sdk.clearMessages();

        sdk.setActiveChannel(Channel.STBH);
        sdk.joinChannel(Channel.STBH, State.JOINING);
        await client.sendActivity('');

        messages = sdk.getChannelMessages(Channel.STBH);
        expect(messages).toHaveLength(1);
        expect(messages[0]).toHaveProperty('activeChannels', [Channel.MH, Channel.STBH]);
        messages = sdk.getChannelMessages(Channel.MH);
        expect(messages).toHaveLength(0);

        activity = sdk.getChannelActivities(Channel.MH)[0];
        expect(activity).toBeUndefined();
        activity = sdk.getChannelActivities(Channel.STBH)[0];
        expect(activity.attachments.find((att) => att.contentType === 'text/html')).toBeUndefined();

        done();
    });
});