Repository: Yoctol/bottender
Pull request #496 (2019-10-30 07:07:36)

add README.md for create-bottender-app
as title


Patch:
diff --git a/packages/bottender/src/router/index.ts b/packages/bottender/src/router/index.ts
index 7bebdddd..80bd2c7c 100644
--- a/packages/bottender/src/router/index.ts
+++ b/packages/bottender/src/router/index.ts
@@ -21,11 +21,9 @@ function router(routes: Route[]) {
       // eslint-disable-next-line no-await-in-loop
       const match = await r.predicate(context);
       if (match) {
-        const derivedProps = match && typeof match === 'object' ? match : {};
-
         return r.action.bind(null, context, {
           ...props,
-          ...derivedProps,
+          match,
         });
       }
     }
@@ -66,12 +64,7 @@ function text(pattern: MatchPattern, action: Action) {
   if (pattern instanceof RegExp) {
     return {
       predicate: (context: Context) => {
-        const match = pattern.exec(context.event.text);
-        return match
-          ? {
-              match,
-            }
-          : false;
+        return pattern.exec(context.event.text);
       },
       action,
     };
@@ -107,14 +100,7 @@ function payload(pattern: MatchPattern, action: Action) {
 
   if (pattern instanceof RegExp) {
     return {
-      predicate: (context: Context) => {
-        const match = pattern.exec(context.event.payload);
-        return match
-          ? {
-              match,
-            }
-          : false;
-      },
+      predicate: (context: Context) => pattern.test(context.event.payload),
       action,
     };
   }
diff --git a/packages/create-bottender-app/README.md b/packages/create-bottender-app/README.md
index 00ccabbd..6b4b34c6 100644
--- a/packages/create-bottender-app/README.md
+++ b/packages/create-bottender-app/README.md
@@ -1,1 +1,109 @@
 # Create Bottender App
+
+Create Bottender apps with predefined configurations.
+
+- [Creating an App]](#creating-an-app) – How to create a new app.
+
+Create Bottender App works on macOS, Windows, and Linux.
+If something doesn’t work, please [file an issue](https://github.com/Yoctol/bottender/issues/new).
+If you have questions or need help, please ask in our [Discord](https://discordapp.com/invite/apNsWBz) community.
+
+## Quick Overview
+
+```sh
+npx create-bottender-app my-app
+cd my-app
+npm start -- --console
+```
+
+## Creating an App
+
+To create a new app, you may choose one of the following methods:
+
+### npx
+
+```sh
+npx create-bottender-app my-app
+```
+
+_([npx](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a package runner tool that comes with npm 5.2+ and higher, see [instructions for older npm versions](https://gist.github.com/chentsulin/0e640176131a250ce583739cb6e49b6a))_
+
+### npm
+
+```sh
+npm init bottender-app my-app
+```
+
+_`npm init <initializer>` is available in npm 6+_
+
+### Yarn
+
+```sh
+yarn create bottender-app my-app
+```
+
+_`yarn create` is available in Yarn 0.25+_
+
+It will create a directory called `my-app` inside the current folder.<br>
+Inside that directory, it will generate the initial project structure and install the transitive dependencies:
+
+```
+my-app
+├── README.md
+├── node_modules
+├── package.json
+├── .gitignore
+├── .env
+├── .env.example
+├── bottender.config.js
+├── index.js
+└── src
+    ├── index.test.js
+    └── index.js
+```
+
+Once the installation is done, you can open your project folder:
+
+```sh
+cd my-app
+```
+
+Inside the newly created project, you can run some built-in commands:
+
+### `npm start` or `yarn start`
+
+Runs the app in production mode.<br>
+By default, server runs on [http://localhost:5000](http://localhost:5000).
+
+To run in "Console Mode", add `--console` as option:
+
+```sh
+npm start -- --console
+yarn start --console
+```
+
+### `npm run dev` or `yarn dev`
+
+Runs the app in development mode.<br>
+The bot will automatically reload if you make changes to the code.<br>
+By default, server runs on [http://localhost:5000](http://localhost:5000) and ngrok runs on [http://localhost:4040](http://localhost:4040).
+
+To run in "Console Mode", add `--console` as option:
+
+```sh
+npm run dev -- --console
+yarn dev --console
+```
+
+### `npm test` or `yarn test`
+
+Runs the test cases using [Jest](https://jestjs.io/).
+
+## What’s Included?
+
+Your environment will have everything you need to build a modern Bottender app:
+
+- Bottender, ES6+ and TypeScript syntax support.
+- A unit test runner - [Jest](https://jestjs.io/) with built-in support for coverage reporting.
+- A JavaScript linter - [Eslint](https://eslint.org/) with some predefined rules.
+- A live development server that warns about common mistakes.


Test patch:
diff --git a/packages/bottender/src/router/__tests__/router.spec.ts b/packages/bottender/src/router/__tests__/router.spec.ts
index ca49582e..77733ef2 100644
--- a/packages/bottender/src/router/__tests__/router.spec.ts
+++ b/packages/bottender/src/router/__tests__/router.spec.ts
@@ -31,7 +31,7 @@ function platformContext(name = '') {
   };
 }
 
-function sendText(message) {
+function textAction(message) {
   return async function(context) {
     await context.sendText(message);
   };
@@ -59,7 +59,7 @@ async function expectPayloadConversation(app, message, expectedReply) {
   }
 }
 
-async function expectPlatformConversation(app, name, expectedReply) {
+async function expectplatformConversation(app, name, expectedReply) {
   const context = platformContext(name);
 
   await app(context);
@@ -76,7 +76,7 @@ describe('#router', () => {
     const Router = router([
       {
         predicate: () => true,
-        action: sendText('hello'),
+        action: textAction('hello'),
       },
     ]);
 
@@ -89,12 +89,12 @@ describe('#router', () => {
     const Router = router([
       {
         predicate: () => false,
-        action: sendText('hello'),
+        action: textAction('hello'),
       },
     ]);
 
     const props = {
-      next: sendText('next'),
+      next: textAction('next'),
     };
 
     const app = run(Router);
@@ -128,13 +128,14 @@ describe('#router', () => {
     expect(receivedContext).toEqual(context);
     expect(receivedProps).toEqual({
       a: 1,
+      match: true,
     });
   });
 });
 
 describe('#route', () => {
   it('should work with *', async () => {
-    const Router = router([route('*', sendText('hello'))]);
+    const Router = router([route('*', textAction('hello'))]);
 
     const app = run(Router);
 
@@ -146,7 +147,7 @@ describe('#route', () => {
     const Router = router([
       route(
         context => context.event.isText && context.event.text.startsWith('h'),
-        sendText('hello')
+        textAction('hello')
       ),
     ]);
 
@@ -159,7 +160,7 @@ describe('#route', () => {
 
 describe('#text', () => {
   it('should work with string', async () => {
-    const Router = router([text('hi', sendText('hello'))]);
+    const Router = router([text('hi', textAction('hello'))]);
 
     const app = run(Router);
 
@@ -169,7 +170,7 @@ describe('#text', () => {
   });
 
   it('should work with array of string', async () => {
-    const Router = router([text(['hi', 'hello'], sendText('hello'))]);
+    const Router = router([text(['hi', 'hello'], textAction('hello'))]);
 
     const app = run(Router);
 
@@ -180,7 +181,7 @@ describe('#text', () => {
   });
 
   it('should work with regexp', async () => {
-    const Router = router([text(/(hi|hello)/, sendText('hello'))]);
+    const Router = router([text(/(hi|hello)/, textAction('hello'))]);
 
     const app = run(Router);
 
@@ -217,7 +218,7 @@ describe('#text', () => {
   });
 
   it('should work with *', async () => {
-    const Router = router([text('*', sendText('hello'))]);
+    const Router = router([text('*', textAction('hello'))]);
 
     const app = run(Router);
 
@@ -230,7 +231,7 @@ describe('#text', () => {
 
 describe('#payload', () => {
   it('should work with string', async () => {
-    const Router = router([payload('hi', sendText('hello'))]);
+    const Router = router([payload('hi', textAction('hello'))]);
 
     const app = run(Router);
 
@@ -241,7 +242,7 @@ describe('#payload', () => {
   });
 
   it('should work with array of string', async () => {
-    const Router = router([payload(['hi', 'hello'], sendText('hello'))]);
+    const Router = router([payload(['hi', 'hello'], textAction('hello'))]);
 
     const app = run(Router);
 
@@ -252,7 +253,7 @@ describe('#payload', () => {
   });
 
   it('should work with regexp', async () => {
-    const Router = router([payload(/(hi|hello)/, sendText('hello'))]);
+    const Router = router([payload(/(hi|hello)/, textAction('hello'))]);
 
     const app = run(Router);
 
@@ -262,34 +263,8 @@ describe('#payload', () => {
     await expectConversation(app, 'hi', null);
   });
 
-  it('should work with regexp match', async () => {
-    let receivedContext;
-    let receivedProps;
-    const action = (ctx, props) => {
-      receivedContext = ctx;
-      receivedProps = props;
-    };
-    const Router = router([payload(/number: (\d+)/, action)]);
-
-    const app = run(Router);
-    const context = payloadContext('number: 123');
-
-    await app(context);
-
-    const match: any = ['number: 123', '123'];
-    match.index = 0;
-    match.input = 'number: 123';
-    match.groups = undefined;
-
-    expect(receivedContext).toEqual(context);
-    expect(receivedProps).toEqual({
-      match,
-      next: undefined,
-    });
-  });
-
   it('should work with *', async () => {
-    const Router = router([payload('*', sendText('hello'))]);
+    const Router = router([payload('*', textAction('hello'))]);
 
     const app = run(Router);
 
@@ -302,42 +277,44 @@ describe('#payload', () => {
 
 describe('#platform', () => {
   it('should work with string', async () => {
-    const Router = router([platform('line', sendText('hello'))]);
+    const Router = router([platform('line', textAction('hello'))]);
 
     const app = run(Router);
 
-    await expectPlatformConversation(app, 'line', 'hello');
-    await expectPlatformConversation(app, 'telegram', null);
-    await expectPlatformConversation(app, 'slack', null);
+    await expectplatformConversation(app, 'line', 'hello');
+    await expectplatformConversation(app, 'telegram', null);
+    await expectplatformConversation(app, 'slack', null);
   });
 
   it('should work with array of string', async () => {
-    const Router = router([platform(['line', 'telegram'], sendText('hello'))]);
+    const Router = router([
+      platform(['line', 'telegram'], textAction('hello')),
+    ]);
 
     const app = run(Router);
 
-    await expectPlatformConversation(app, 'line', 'hello');
-    await expectPlatformConversation(app, 'telegram', 'hello');
-    await expectPlatformConversation(app, 'slack', null);
+    await expectplatformConversation(app, 'line', 'hello');
+    await expectplatformConversation(app, 'telegram', 'hello');
+    await expectplatformConversation(app, 'slack', null);
   });
 
   it('should work with regexp', async () => {
-    const Router = router([platform(/(line|telegram)/, sendText('hello'))]);
+    const Router = router([platform(/(line|telegram)/, textAction('hello'))]);
 
     const app = run(Router);
 
-    await expectPlatformConversation(app, 'line', 'hello');
-    await expectPlatformConversation(app, 'telegram', 'hello');
-    await expectPlatformConversation(app, 'slack', null);
+    await expectplatformConversation(app, 'line', 'hello');
+    await expectplatformConversation(app, 'telegram', 'hello');
+    await expectplatformConversation(app, 'slack', null);
   });
 
   it('should work with *', async () => {
-    const Router = router([platform('*', sendText('hello'))]);
+    const Router = router([platform('*', textAction('hello'))]);
 
     const app = run(Router);
 
-    await expectPlatformConversation(app, 'line', 'hello');
-    await expectPlatformConversation(app, 'telegram', 'hello');
-    await expectPlatformConversation(app, 'slack', 'hello');
+    await expectplatformConversation(app, 'line', 'hello');
+    await expectplatformConversation(app, 'telegram', 'hello');
+    await expectplatformConversation(app, 'slack', 'hello');
   });
 });