Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Full stack/refactor router pt2 - split and sort tests #1937

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
4 changes: 2 additions & 2 deletions packages/botonic-core/src/models/legacy-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export interface BotRequest {
input: Input
lastRoutePath: RoutePath
session: Session
dataProvider: DataProvider
dataProvider?: DataProvider
}

/** The response of the bot for the triggered actions, which can be
Expand Down Expand Up @@ -274,7 +274,7 @@ export interface RouteParams {
}
export interface PathParams {
path: RoutePath
params: string | undefined
params: Params
}

export type MatchingProp =
Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/src/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function runPlugins(
lastRoutePath: RoutePath,
response: string | null = null,
messageEvents: Partial<BotonicEvent>[] | null = null,
dataProvider: DataProvider
dataProvider?: DataProvider
): Promise<void> {
for (const key in plugins) {
const p = await plugins[key]
Expand Down
4 changes: 2 additions & 2 deletions packages/botonic-core/src/routing/router-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function isPathPayload(payload?: string): boolean {
export function getPathParamsFromPathPayload(payload?: string): PathParams {
const defaultPathParams = {
path: null,
params: undefined,
params: {},
}
if (!payload) return defaultPathParams
if (!isPathPayload(payload)) return defaultPathParams
Expand All @@ -37,7 +37,7 @@ export function getPathParamsFromPathPayload(payload?: string): PathParams {
throw `${PATH_PAYLOAD_IDENTIFIER} is empty`
}
const [path, params] = pathWithParams.split('?')
return { path: path ?? null, params }
return { path: path ?? null, params: pathParamsToParams(params) }
} catch (e) {
console.error('Error getting path and params from input.payload:', e)
return defaultPathParams
Expand Down
11 changes: 4 additions & 7 deletions packages/botonic-core/src/routing/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,7 @@ export class Router {
*/
if (matchedRoute && matchedRoute.redirect) {
session.__retries = 0
const redirectionRoute = this.getRouteByPath(
matchedRoute.redirect,
this.routes
)
const redirectionRoute = this.getRouteByPath(matchedRoute.redirect)
if (redirectionRoute) {
return {
action: redirectionRoute.action,
Expand Down Expand Up @@ -292,7 +289,7 @@ export class Router {
session: Session,
lastRoutePath: RoutePath
): RoutingState {
const currentRoute = this.getRouteByPath(lastRoutePath, this.routes)
const currentRoute = this.getRouteByPath(lastRoutePath)
if (currentRoute && lastRoutePath) currentRoute.path = lastRoutePath
if (typeof input.payload === 'string' && isPathPayload(input.payload)) {
return this.getRoutingStateFromPathPayload(currentRoute, input.payload)
Expand Down Expand Up @@ -341,7 +338,7 @@ export class Router {
session,
currentRoute?.path ?? null
)
const isFlowBroken = !currentRoute?.path ? false : true
const isFlowBroken = Boolean(currentRoute?.path)
if (routeParams?.route) {
return {
currentRoute,
Expand Down Expand Up @@ -385,7 +382,7 @@ export class Router {
return {
currentRoute,
matchedRoute: { ...matchedRoute, path: seekPath },
params: pathParamsToParams(params),
params,
isFlowBroken: false,
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BotonicOutputParser } from '../src/output-parser'
import { BotonicOutputParser } from '../../src/output-parser'

// @ts-ignore
const { expect } = global
Expand Down
17 changes: 17 additions & 0 deletions packages/botonic-core/tests/helpers/routing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { PATH_PAYLOAD_IDENTIFIER, PROVIDER, Session } from '../../src'

export function testRoute(): any {
return {}
}

export function testSession(): Session {
return {
user: { id: 'userid', provider: PROVIDER.DEV },
bot: { id: 'bot_id' },
is_first_interaction: true,
__retries: 0,
}
}

export const createPathPayload = (pathWithParams: string): string =>
`${PATH_PAYLOAD_IDENTIFIER}${pathWithParams}`
2 changes: 1 addition & 1 deletion packages/botonic-core/tests/parsing/carousel.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BotonicOutputParserTester } from '../helpers'
import { BotonicOutputParserTester } from '../helpers/parsing'

const tester = new BotonicOutputParserTester()
describe('Parsing Carousel responses', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/tests/parsing/custom.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BotonicOutputParserTester } from '../helpers'
import { BotonicOutputParserTester } from '../helpers/parsing'

const tester = new BotonicOutputParserTester()
describe('Parsing Location responses', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/tests/parsing/location.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BotonicOutputParserTester } from '../helpers'
import { BotonicOutputParserTester } from '../helpers/parsing'

// TODO: Location Messages coming without typing nor delay

Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/tests/parsing/media.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BotonicOutputParserTester } from '../helpers'
import { BotonicOutputParserTester } from '../helpers/parsing'

const tester = new BotonicOutputParserTester()
describe('Parsing Media responses', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/tests/parsing/text.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable jest/expect-expect */
import { PATH_PAYLOAD_IDENTIFIER } from '../../src'
import { BotonicOutputParserTester } from '../helpers'
import { BotonicOutputParserTester } from '../helpers/parsing'

// TODO: Test with Markdown, test with Markdown + new lines

Expand Down
2 changes: 1 addition & 1 deletion packages/botonic-core/tests/parsing/user-events.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BotonicOutputParserTester } from '../helpers'
import { BotonicOutputParserTester } from '../helpers/parsing'

const tester = new BotonicOutputParserTester()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Router } from '../../src'

describe('TEST: Get route by path', () => {
const externalRoutes = [
{ path: '', action: 'Flow1.2' },
{ path: 'child', action: 'ChildAction' },
]
const router = new Router([
{ path: 'initial', action: 'Initial' },
{
path: 'flow-1',
action: 'Flow1',
childRoutes: [
{
path: '1',
action: 'Flow1.1',
childRoutes: [
{ path: '1', action: 'Flow1.1.1' },
{ path: '2', action: 'Flow1.1.2' },
{ path: '3', action: 'Flow1.1.3' },
],
},
{ path: '2', childRoutes: externalRoutes },
{
path: '3',
action: 'Flow1.3',
childRoutes: [
{ path: '1', action: 'Flow1.3.1' },
{ path: '2', action: 'Flow1.3.2' },
{ path: '3', action: 'Flow1.3.3' },
],
},
],
},
{ path: '404', action: '404Action' },
])

it('path exists', () => {
expect(router.getRouteByPath('initial')).toEqual({
path: 'initial',
action: 'Initial',
})
expect(router.getRouteByPath('flow-1/1')).toEqual({
path: '1',
action: 'Flow1.1',
childRoutes: [
{ path: '1', action: 'Flow1.1.1' },
{ path: '2', action: 'Flow1.1.2' },
{ path: '3', action: 'Flow1.1.3' },
],
})
expect(router.getRouteByPath('flow-1/3/2')).toEqual({
action: 'Flow1.3.2',
path: '2',
})
})
it('path exists in composed child routes', () => {
expect(router.getRouteByPath('flow-1/2')).toEqual({
path: '2',
childRoutes: [
{ action: 'Flow1.2', path: '' },
{ action: 'ChildAction', path: 'child' },
],
})
expect(router.getRouteByPath('flow-1/2/child')).toEqual({
path: 'child',
action: 'ChildAction',
})
})
it('path does not exist', () => {
expect(router.getRouteByPath('')).toBeNull()
expect(router.getRouteByPath('foo')).toBeNull()
expect(router.getRouteByPath('flow-1/3/2/6')).toBeNull()
})
})
172 changes: 172 additions & 0 deletions packages/botonic-core/tests/routing/router.match-route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { BotRequest, Input } from '../../src'
import { Router } from '../../src/routing'
import { testRoute, testSession } from '../helpers/routing'

const textInput: Input = { type: 'text', text: 'hi' }
const textInputComplex: Input = { type: 'text', text: 'Cömplêx input &% 🚀' }
const textPayloadInput: Input = { type: 'text', text: 'hi', payload: 'foo' }
const postbackInput: Input = { type: 'postback', payload: 'foo' }

const audioInput: Input = {
type: 'audio',
src: 'data:audio/mpeg;base64,iVBORw0KG',
}
const documentInput: Input = {
type: 'document',
src: 'data:application/pdf;base64,iVBORw0KG',
}
const imageInput: Input = {
type: 'image',
src: 'data:image/png;base64,iVBORw0KG',
}
const videoInput: Input = {
type: 'video',
src: 'data:video/mp4;base64,iVBORw0KG',
}

const requestInput: BotRequest = {
input: textInput,
session: { ...testSession(), organization: 'myOrg' },
lastRoutePath: 'initial',
}

describe('TEST: Match route by MATCHER <> INPUT', () => {
const router = new Router([])
const matchTextProp = (matcher, textInput) =>
router.matchRoute(
testRoute(),
'text',
matcher,
textInput,
testSession(),
null
)
const matchPayloadProp = (matcher, payload) =>
router.matchRoute(
testRoute(),
'payload',
matcher,
payload,
testSession(),
null
)
const matchTypeProp = (type, input) =>
router.matchRoute(testRoute(), 'type', type, input, testSession(), null)
const matchRequestProp = (matcher, request) =>
router.matchRoute(
testRoute(),
'request',
matcher,
request.input,
request.session,
request.lastRoutePath
)
it('text <> text', () => {
expect(matchTextProp('hi', textInput)).toBeTruthy()
expect(matchTextProp('hii', textInput)).toBeFalsy()
expect(matchTextProp('bye', textInput)).toBeFalsy()
expect(matchTextProp('', textInput)).toBeFalsy()
expect(matchTextProp(null, textInput)).toBeFalsy()
expect(matchTextProp('Cömplêx input &% 🚀', textInputComplex)).toBeTruthy()
expect(matchTextProp(' Cömplêx input &% 🚀', textInputComplex)).toBeFalsy() // has a space at the beginning
})
it('regex <> text', () => {
expect(matchTextProp(/hi/, textInput)).toBeTruthy()
expect(matchTextProp(/bye/, textInput)).toBeFalsy()
expect(matchTextProp(/🚀/, textInputComplex)).toBeTruthy()
expect(matchTextProp(/complex/, textInputComplex)).toBeFalsy()
})
it('function <> text', () => {
expect(matchTextProp(v => v.startsWith('hi'), textInput)).toBeTruthy()
expect(matchTextProp(v => !v.startsWith('hi'), textInput)).toBeFalsy()
})
it('input <> text', () => {
expect(
router.matchRoute(
testRoute(),
'input',
i => i.text.startsWith('hi'),
textInput,
testSession(),
null
)
).toBeTruthy()
expect(
router.matchRoute(
testRoute(),
'input',
i => !i.text.startsWith('hi'),
textInput,
testSession(),
null
)
).toBeFalsy()
})
it('text <> text payload', () => {
expect(matchPayloadProp('foo', textPayloadInput)).toBeTruthy()
expect(matchPayloadProp('fooo', textPayloadInput)).toBeFalsy()
expect(matchPayloadProp('bar', textPayloadInput)).toBeFalsy()
expect(matchPayloadProp('', textPayloadInput)).toBeFalsy()
expect(matchPayloadProp(null, textPayloadInput)).toBeFalsy()
})
it('regex <> text payload', () => {
expect(matchPayloadProp(/foo/, textPayloadInput)).toBeTruthy()
expect(matchPayloadProp(/bar/, textPayloadInput)).toBeFalsy()
})
it('function <> text payload', () => {
expect(
matchPayloadProp(v => v.startsWith('fo'), textPayloadInput)
).toBeTruthy()
expect(
matchPayloadProp(v => !v.startsWith('fo'), textPayloadInput)
).toBeFalsy()
})
it('text <> postback', () => {
expect(matchPayloadProp('foo', postbackInput)).toBeTruthy()
expect(matchPayloadProp('fooo', postbackInput)).toBeFalsy()
expect(matchPayloadProp('bar', postbackInput)).toBeFalsy()
expect(matchPayloadProp('', postbackInput)).toBeFalsy()
expect(matchPayloadProp(null, postbackInput)).toBeFalsy()
})
it('regex <> postback', () => {
expect(matchPayloadProp(/foo/, postbackInput)).toBeTruthy()
expect(matchPayloadProp(/bar/, postbackInput)).toBeFalsy()
})
it('function <> postback', () => {
expect(
matchPayloadProp(v => v.startsWith('fo'), postbackInput)
).toBeTruthy()
expect(
matchPayloadProp(v => !v.startsWith('fo'), postbackInput)
).toBeFalsy()
})
it('function <> request', () => {
expect(
matchRequestProp(
request =>
request.input.text === 'hi' &&
request.session.organization === 'myOrg' &&
request.lastRoutePath === 'initial',
requestInput
)
).toBeTruthy()
expect(
matchRequestProp(
request =>
request.input.text === 'hello' &&
request.session.organization === 'myOrg' &&
request.lastRoutePath === 'initial',
requestInput
)
).toBeFalsy()
})
it('type <> audio, document, image, video', () => {
expect(matchTypeProp('audio', audioInput)).toBeTruthy()
expect(matchTypeProp('document', documentInput)).toBeTruthy()
expect(matchTypeProp('image', imageInput)).toBeTruthy()
expect(matchTypeProp('video', videoInput)).toBeTruthy()
})
it('type <> other inputs', () => {
expect(matchTypeProp(/.*/, { type: 'anyOtherInput' })).toBeTruthy()
})
})
Loading