update: separate stateful and stateless scenarios
This commit is contained in:
parent
5d7a85b729
commit
97098482af
36
README.md
36
README.md
@ -1,6 +1,6 @@
|
||||
# cic-ussd-e2e
|
||||
|
||||
> Mocking user interaction with cic-ussd
|
||||
Bunch of test scripts to automate traversing USSD menu's with little human interaction and as close replication to actual USSD usage including AT style reqests and waiting times.
|
||||
|
||||
## Setup
|
||||
|
||||
@ -15,19 +15,39 @@
|
||||
|
||||
- Node.js
|
||||
- Install dependencies with `$ npm i`
|
||||
- Set global config in `config.js`
|
||||
|
||||
## Scenarios
|
||||
|
||||
| Test | Type | Description |
|
||||
| -------------- | --------- | ---------------------------------------------- |
|
||||
| initial_menu | Statless | Main menu > Help > Exit |
|
||||
| display_sarafu | Stateless | Main menu > My Sarafu > Exit |
|
||||
| reset_pin | Stateful | Main menu > Account options > Reset pin > Exit |
|
||||
|
||||
### Stateful scenarios
|
||||
|
||||
These tests live individually in their own folder. They require some ENV variables to be set before running them.
|
||||
|
||||
### Stateless scenarios
|
||||
|
||||
These are included under the `stateless` folder and can be run in any order and have multple tests under the same folder since they only check correctness of menu responses.
|
||||
|
||||
## Running tests
|
||||
|
||||
```bash
|
||||
$ npm run test
|
||||
# Set env variables for stateful scenarios, refer to individual test folders
|
||||
$ npx tap --bail scenarios/SCENARIO_NAME
|
||||
```
|
||||
|
||||
## Scenarios
|
||||
Examples:
|
||||
|
||||
| Test | Description |
|
||||
| ---------------- | ---------------------------- |
|
||||
| 1_initial_menu | Main menu > Help > Exit |
|
||||
| 2_display_sarafu | Main menu > My Sarafu > Exit |
|
||||
- `CURRENT_PIN=4444 NEW_PIN=3333 npx tap --bail scenarios/stateless`
|
||||
|
||||
### Limitations
|
||||
|
||||
- These scripts cannot reset USSD sessions upon failure. You will need to manually set your account USSD menu to the main menu
|
||||
- Cannot verify sms status, confirm with cic-notify for delivery
|
||||
|
||||
### Writing your own scenario
|
||||
|
||||
@ -35,6 +55,8 @@ $ npm run test
|
||||
- Control wait between menus with the `wait(time in ms)` function
|
||||
- Follow any spec
|
||||
- For any menu traversal, remember to append a `*` before the next input, this correctly mocks AT webhooks e.g.
|
||||
- Dyanmic inputs for stateful scenarios must come from an env var that can be set again on next run
|
||||
- Include a `tap-parallel-not-ok` file in any new folder
|
||||
|
||||
```bash
|
||||
# Initial Main menu, no input
|
||||
|
13
config.js
Normal file
13
config.js
Normal file
@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
ussd: {
|
||||
serviceCode: "*483*061#",
|
||||
endpoint: "https://ussd.grassecon.net",
|
||||
timeout: 3000,
|
||||
},
|
||||
test: {
|
||||
waitNextMenu: 3000,
|
||||
},
|
||||
user: {
|
||||
ussdPhone: "254711777734",
|
||||
},
|
||||
};
|
14
lib.js
14
lib.js
@ -1,17 +1,19 @@
|
||||
const crypto = require("crypto");
|
||||
const phin = require("phin");
|
||||
|
||||
async function ussdClient(sessionId, input = "") {
|
||||
const conf = require("./config");
|
||||
|
||||
async function ussdClient(phone, sessionId, input = "") {
|
||||
const requestOptions = {
|
||||
url: "https://ussd.grassecon.net",
|
||||
url: conf.ussd.endpoint,
|
||||
method: "POST",
|
||||
parse: "string",
|
||||
timeout: 3000,
|
||||
timeout: conf.ussd.timeout,
|
||||
form: {
|
||||
sessionId: sessionId,
|
||||
// Get from a config file, then pass as a param
|
||||
phoneNumber: "254711777734",
|
||||
serviceCode: "*483*061#",
|
||||
// Get from a conf file, then pass as a param
|
||||
phoneNumber: phone,
|
||||
serviceCode: conf.ussd.serviceCode,
|
||||
text: input,
|
||||
},
|
||||
};
|
||||
|
113
scenarios/reset_pin/reset_pin.test.js
Normal file
113
scenarios/reset_pin/reset_pin.test.js
Normal file
@ -0,0 +1,113 @@
|
||||
const test = require("tap").test;
|
||||
const rangi = require("rangi");
|
||||
|
||||
const lib = require("../../lib");
|
||||
const conf = require("../../config");
|
||||
|
||||
test("Reset Pin", async (t) => {
|
||||
t.before((t) => {
|
||||
if (!process.env.CURRENT_PIN || !process.env.NEW_PIN) {
|
||||
t.bailout("ENV not set");
|
||||
}
|
||||
});
|
||||
|
||||
const sessionId = lib.newSession();
|
||||
const currentPin = process.env.CURRENT_PIN;
|
||||
const newPin = process.env.NEW_PIN;
|
||||
|
||||
t.test("Display menu and Sarafu balance", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /Balance/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Go to My Accont menu", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "3");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /PIN options/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Go to Pin options", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "3*5");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /Change my PIN/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Start Pin Change", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "3*5*1");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /Enter current PIN/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Enter current Pin", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(
|
||||
conf.user.ussdPhone,
|
||||
sessionId,
|
||||
`3*5*1*${currentPin}`
|
||||
);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /new four number PIN/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Enter new Pin", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(
|
||||
conf.user.ussdPhone,
|
||||
sessionId,
|
||||
`3*5*1*${currentPin}*${newPin}`
|
||||
);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /new four number PIN again/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Enter new Pin agin", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(
|
||||
conf.user.ussdPhone,
|
||||
sessionId,
|
||||
`3*5*1*${currentPin}*${newPin}*${newPin}`
|
||||
);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
t.match(r.text, /Your request has been sent/g);
|
||||
t.end();
|
||||
});
|
||||
|
||||
t.test("Exit", async (t) => {
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(
|
||||
conf.user.ussdPhone,
|
||||
sessionId,
|
||||
`3*5*1*${currentPin}*${newPin}*${newPin}*99`
|
||||
);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "END");
|
||||
t.match(r.text, /Thank/g);
|
||||
t.end();
|
||||
});
|
||||
});
|
@ -1,16 +1,17 @@
|
||||
const test = require("tap").test;
|
||||
const rangi = require("rangi");
|
||||
|
||||
const lib = require("../lib");
|
||||
const lib = require("../../lib");
|
||||
const conf = require("../../config");
|
||||
|
||||
test("Initial menu, display balances, exit", async (t) => {
|
||||
test("Display Sarafu", async (t) => {
|
||||
const sessionId = lib.newSession();
|
||||
|
||||
t.plan(3);
|
||||
|
||||
t.test("Display menu and Sarafu balance", async (t) => {
|
||||
await lib.wait(3000);
|
||||
const r = await lib.ussdClient(sessionId);
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
@ -19,8 +20,8 @@ test("Initial menu, display balances, exit", async (t) => {
|
||||
});
|
||||
|
||||
t.test("Go to My Sarafu menu", async (t) => {
|
||||
await lib.wait(3000);
|
||||
const r = await lib.ussdClient(sessionId, "2");
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "2");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
@ -29,8 +30,8 @@ test("Initial menu, display balances, exit", async (t) => {
|
||||
});
|
||||
|
||||
t.test("Exit", async (t) => {
|
||||
await lib.wait(3000);
|
||||
const r = await lib.ussdClient(sessionId, "2*00");
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "2*00");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "END");
|
@ -1,16 +1,17 @@
|
||||
const test = require("tap").test;
|
||||
const rangi = require("rangi");
|
||||
|
||||
const lib = require("../lib");
|
||||
const lib = require("../../lib");
|
||||
const conf = require("../../config");
|
||||
|
||||
test("Initial menu, go to help, exit", async (t) => {
|
||||
test("Initial Menu", async (t) => {
|
||||
const sessionId = lib.newSession();
|
||||
|
||||
t.plan(3);
|
||||
|
||||
t.test("Display menu and Sarafu balance", async (t) => {
|
||||
await lib.wait(3000);
|
||||
const r = await lib.ussdClient(sessionId);
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId);
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
@ -19,8 +20,8 @@ test("Initial menu, go to help, exit", async (t) => {
|
||||
});
|
||||
|
||||
t.test("Go to help menu", async (t) => {
|
||||
await lib.wait(3000);
|
||||
const r = await lib.ussdClient(sessionId, "4");
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "4");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "CON");
|
||||
@ -29,8 +30,8 @@ test("Initial menu, go to help, exit", async (t) => {
|
||||
});
|
||||
|
||||
t.test("Exit", async (t) => {
|
||||
await lib.wait(3000);
|
||||
const r = await lib.ussdClient(sessionId, "4*99");
|
||||
await lib.wait(conf.test.waitNextMenu);
|
||||
const r = await lib.ussdClient(conf.user.ussdPhone, sessionId, "4*99");
|
||||
console.log(rangi.cyan(r.text));
|
||||
|
||||
t.equal(r.code, "END");
|
0
scenarios/stateless/tap-parallel-not-ok
Normal file
0
scenarios/stateless/tap-parallel-not-ok
Normal file
@ -448,7 +448,7 @@ browserslist@^4.17.5:
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88"
|
||||
integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001317"
|
||||
caniuse-lite "^1.0.config.test.waitNextMenu1317"
|
||||
electron-to-chromium "^1.4.84"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^2.0.2"
|
||||
@ -493,9 +493,9 @@ camelcase@^5.0.0, camelcase@^5.3.1:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
caniuse-lite@^1.0.30001317:
|
||||
version "1.0.30001320"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001320.tgz#8397391bec389b8ccce328636499b7284ee13285"
|
||||
caniuse-lite@^1.0.config.test.waitNextMenu1317:
|
||||
version "1.0.config.test.waitNextMenu1320"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.config.test.waitNextMenu1320.tgz#8397391bec389b8ccce328636499b7284ee13285"
|
||||
integrity sha512-MWPzG54AGdo3nWx7zHZTefseM5Y1ccM7hlQKHRqJkPozUaw3hNbBTMmLn16GG2FUzjR13Cr3NPfhIieX5PzXDA==
|
||||
|
||||
cardinal@^2.1.1:
|
||||
|
Reference in New Issue
Block a user