Add tests and detect WhatsApp Web updates (#686)

* test setup, add initializer tests

* test sending messages

* add script to check latest version

* add github action

* use env vars

* configure environment with .env file

* add test for sticker name and author

* add DownloadManager model

* test chats and contacts

* test for number utility functions

* throw error if no remote id has been set

* Update .version
This commit is contained in:
Pedro S. Lopez
2021-07-16 02:50:05 -04:00
committed by GitHub
parent 04d2308bbc
commit a03cc41d22
10 changed files with 574 additions and 6 deletions

2
.env.example Normal file
View File

@@ -0,0 +1,2 @@
WWEBJS_TEST_SESSION_PATH=test_session.json
WWEBJS_TEST_REMOTE_ID=XXXXXXXXXX@c.us

View File

@@ -5,7 +5,7 @@
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"extends": ["eslint:recommended", "plugin:mocha/recommended"],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
@@ -13,6 +13,7 @@
"parserOptions": {
"ecmaVersion": 2020
},
"plugins": ["mocha"],
"ignorePatterns": ["docs"],
"rules": {
"indent": [

35
.github/workflows/update.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: Update
on:
schedule:
- cron: "0/15 * * * *"
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install node v14
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm install
- run: cd ./tools/version-checker
- name: Run Updater
run: ./update-version
- name: Store WA Version
run: echo WA_VERSION=`cat ./.version` >> $GITHUB_ENV
- name: Create Pull Request
uses: peter-evans/create-pull-request@v3
with:
commit-message: Update supported WhatsApp Web version to v${{ env.WA_VERSION }}
title: Update WhatsApp Web Version (${{ env.WA_VERSION }})
body: |
A new version of WhatsApp Web has been detected!
Tests should be run against this new version before merging.
labels: WhatsApp Change
reviewers: pedroslopez

View File

@@ -5,7 +5,7 @@
"main": "./index.js",
"typings": "./index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "mocha tests",
"shell": "node --experimental-repl-await ./shell.js",
"generate-docs": "node_modules/.bin/jsdoc --configure .jsdoc.json --verbose"
},
@@ -37,8 +37,13 @@
"sharp": "^0.28.3"
},
"devDependencies": {
"chai": "^4.3.4",
"dotenv": "^10.0.0",
"eslint": "^7.27.0",
"eslint-plugin-mocha": "^9.0.0",
"jsdoc": "^3.6.4",
"jsdoc-baseline": "^0.1.5"
"jsdoc-baseline": "^0.1.5",
"mocha": "^8.4.0",
"sinon": "^11.1.1"
}
}

View File

@@ -294,7 +294,8 @@ exports.LoadUtils = () => {
};
window.WWebJS.getChat = async chatId => {
const chat = window.Store.Chat.get(chatId);
const chatWid = window.Store.WidFactory.createWid(chatId);
const chat = await window.Store.Chat.find(chatWid);
return await window.WWebJS.getChatModel(chat);
};
@@ -324,8 +325,9 @@ exports.LoadUtils = () => {
return res;
};
window.WWebJS.getContact = contactId => {
const contact = window.Store.Contact.get(contactId);
window.WWebJS.getContact = async contactId => {
const wid = window.Store.WidFactory.createWid(contactId);
const contact = await window.Store.Contact.find(wid);
return window.WWebJS.getContactModel(contact);
};

10
tests/README.md Normal file
View File

@@ -0,0 +1,10 @@
## Running tests
These tests require an authenticated WhatsApp Web session, as well as an additional phone that you can send messages to.
This can be configured using the following environment variables:
- `WWEBJS_TEST_SESSION`: A JSON-formatted string with the session details. Must include `WABrowserId`, `WASecretBundle`, `WAToken1` and `WAToken2`.
- `WWEBJS_TEST_SESSION_PATH`: Path to a JSON file that contains the session details. Must include `WABrowserId`, `WASecretBundle`, `WAToken1` and `WAToken2`.
- `WWEBJS_TEST_REMOTEID`: A valid WhatsApp ID that you can send messages to, e.g. `123456789@c.us`.
You *must* set `WWEBJS_TEST_REMOTEID` **and** either `WWEBJS_TEST_SESSION` or `WWEBJS_TEST_SESSION_PATH` for the tests to run properly.

417
tests/client.js Normal file
View File

@@ -0,0 +1,417 @@
const {expect} = require('chai');
const sinon = require('sinon');
const helper = require('./helper');
const Chat = require('../src/structures/Chat');
const Contact = require('../src/structures/Contact');
const Message = require('../src/structures/Message');
const MessageMedia = require('../src/structures/MessageMedia');
const Location = require('../src/structures/Location');
const { MessageTypes } = require('../src/util/Constants');
const remoteId = helper.remoteId;
describe('Client', function() {
describe('Authentication', function() {
it('should emit QR code if not authenticated', async function() {
this.timeout(25000);
const callback = sinon.spy();
const client = helper.createClient();
client.on('qr', callback);
client.initialize();
await helper.sleep(20000);
expect(callback.called).to.equal(true);
expect(callback.args[0][0]).to.have.lengthOf(152);
await client.destroy();
});
it('should fail auth if session is invalid', async function() {
this.timeout(40000);
const authFailCallback = sinon.spy();
const qrCallback = sinon.spy();
const readyCallback = sinon.spy();
const client = helper.createClient({
options: {
session: {
WABrowserId: 'invalid',
WASecretBundle: 'invalid',
WAToken1: 'invalid',
WAToken2: 'invalid'
},
authTimeoutMs: 10000,
restartOnAuthFail: false
}
});
client.on('qr', qrCallback);
client.on('auth_failure', authFailCallback);
client.on('ready', readyCallback);
client.initialize();
await helper.sleep(25000);
expect(authFailCallback.called).to.equal(true);
expect(authFailCallback.args[0][0]).to.equal('Unable to log in. Are the session details valid?');
expect(readyCallback.called).to.equal(false);
expect(qrCallback.called).to.equal(false);
await client.destroy();
});
it('can restart without a session if session was invalid and restartOnAuthFail=true', async function() {
this.timeout(40000);
const authFailCallback = sinon.spy();
const qrCallback = sinon.spy();
const client = helper.createClient({
options:{
session: {
WABrowserId: 'invalid',
WASecretBundle: 'invalid',
WAToken1: 'invalid',
WAToken2: 'invalid'
},
authTimeoutMs: 10000,
restartOnAuthFail: true
}
});
client.on('auth_failure', authFailCallback);
client.on('qr', qrCallback);
client.initialize();
await helper.sleep(35000);
expect(authFailCallback.called).to.equal(true);
expect(qrCallback.called).to.equal(true);
expect(qrCallback.args[0][0]).to.have.lengthOf(152);
await client.destroy();
});
it('should authenticate with existing session', async function() {
this.timeout(40000);
const authenticatedCallback = sinon.spy();
const qrCallback = sinon.spy();
const readyCallback = sinon.spy();
const client = helper.createClient({withSession: true});
client.on('qr', qrCallback);
client.on('authenticated', authenticatedCallback);
client.on('ready', readyCallback);
await client.initialize();
expect(authenticatedCallback.called).to.equal(true);
const newSession = authenticatedCallback.args[0][0];
expect(newSession).to.have.key([
'WABrowserId',
'WASecretBundle',
'WAToken1',
'WAToken2'
]);
expect(authenticatedCallback.called).to.equal(true);
expect(readyCallback.called).to.equal(true);
expect(qrCallback.called).to.equal(false);
await client.destroy();
});
});
describe('Authenticated', function() {
let client;
before(async function() {
this.timeout(35000);
client = helper.createClient({withSession: true});
await client.initialize();
});
after(async function () {
await client.destroy();
});
describe('Expose Store', function() {
it('exposes the store', async function() {
const exposed = await client.pupPage.evaluate(() => {
return Boolean(window.Store);
});
expect(exposed).to.equal(true);
});
it('exposes all required WhatsApp Web internal models', async function() {
const expectedModules = [
'Chat',
'Msg',
'Contact',
'Conn',
'AppState',
'CryptoLib',
'Wap',
'SendSeen',
'SendClear',
'SendDelete',
'genId',
'SendMessage',
'MsgKey',
'Invite',
'OpaqueData',
'MediaPrep',
'MediaObject',
'MediaUpload',
'Cmd',
'MediaTypes',
'VCard',
'UserConstructor',
'Validators',
'WidFactory',
'BlockContact',
'GroupMetadata',
'Sticker',
'UploadUtils',
'Label',
'Features',
'QueryOrder',
'QueryProduct',
'DownloadManager'
];
const loadedModules = await client.pupPage.evaluate(() => {
return Object.keys(window.Store);
});
expect(loadedModules).to.include.members(expectedModules);
});
});
describe('Send Messages', function () {
it('can send a message', async function() {
const msg = await client.sendMessage(remoteId, 'hello world');
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.TEXT);
expect(msg.fromMe).to.equal(true);
expect(msg.body).to.equal('hello world');
expect(msg.to).to.equal(remoteId);
});
it('can send a media message', async function() {
const media = new MessageMedia(
'image/png',
'iVBORw0KGgoAAAANSUhEUgAAAV4AAACWBAMAAABkyf1EAAAAG1BMVEXMzMyWlpacnJyqqqrFxcWxsbGjo6O3t7e+vr6He3KoAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEcElEQVR4nO2aTW/bRhCGh18ij1zKknMkbbf2UXITIEeyMhIfRaF1exQLA/JRclslRykO+rs7s7s0VwytNmhJtsA8gHZEcox9PTs7uysQgGEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmGYr2OWRK/ReIKI8Zt7Hb19wTcQ0uTkGh13bQupcw7gPOvdo12/5CzNtNR7xLUtNtT3CGBQ6g3InjY720pvofUec22LJPr8PhEp2OMPyI40PdwWUdronCu9yQpdPx53bQlfLKnfOVhlnDYRBXve4Ov+IZTeMgdedm0NR+xoXJeQvdJ3CvziykSukwil16W/Oe7aGjIjqc/9ib4jQlJy0uArtN4A0+cvXFvDkmUJ47sJ1Y1ATLDNVXZkNPIepQzxy1ki9fqiwbUj/I+64zxWNzyZnPuhvohJ9K70VvXBixpcu2SAHU+Xd9EKdEJDNpYP3AQr3bQSpPQ6Y6/4dl1z7ZDbArsszjA7L0g7ibB0CDcidUWVoErvIMKZh2Xs0LUzcLW6V5NfiUgNEbaYmAVL6bXl0nJRc+1S72ua/D/cTjGPlQj7eUqd7A096rYlRjdPYlhz7VIvxpVG3cemDKF+WAwLY/6XelOZKTXXzsC4xvDjjtSN6kHLhLke6PrwM8h1raf40qjrGO7H9aTEbduucjS04ZrYU/4iuS5Z2Hdt0rvCLFdmLEXcU30AGddST62o+sLcf5l6k7CP+ru4pLYqX/VFyxbm/utQbx/r22ZEbTb2f5I2kns1Y1OQR8ZyofX+TjJxj1Rz7QQVnf1QzR26Oth0ueJVYcRP6ZUPac/Rx/5M6ixO1dhSrT3Y1DpiYmx3tF4ZUdpz9LD/dSg9PXES0LB71BwcGjKROuV28lnvnv7HHJsezheBGH5+X2CfSfRbMKW+5aGs3JFjMrjGibJc0S7TJzqjHrh2hDybj9XRXNZa89Aro55XBdbW5wti2c/5WJ7jJ1RolVUn/HWpb0I58Tziup6Rx7Dm2hnbRP1GM9PW/NFmQ4PtVRVN63Wvxfmu5sowDMMwDMMwDMMwDMMwDMMwDMMwzL+CpT//F/6beoV8zb2Jmt4Qryx6lTUCsENQ75HOkhXAO3EPVgyQtKtUy3C/e+FJg17Zjnew1Xrdb9InbG4WqfUAftG+WhLwPVyfg536+MU7m4C1CMk4ZznpXZzDYI1PDL2nS1hpvc5cNd7E2sJg05Fe7/7d3Fln8Cvc3bwB616auxsKl4WPghjemHrDqyDWeu1UNW5s2btPnSQ75oOdunEwWazfwgVG0kqluYCM9OIjWOGnfA2b9G4Ha63XKpvQ8perTvTifJNhi6+WMWmi7smEZf6G8MmhlyGq+NqP8GV84TLuJr7UIQVx+bDEoEpRZIz42gs40OuN4Mv8hXzelV7KX1isH+ewTWckikyVv+CfHuqVF7I16gN0VKypX6wPsE+zFPzkinolU9UH8OMGvSpnZqKsv13p/RsMun6X5x/y2LeAr8O66lsBwzBMP/wJfyGq8pgBk6IAAAAASUVORK5CYII='
);
const msg = await client.sendMessage(remoteId, media, {caption: 'here\'s my media'});
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.IMAGE);
expect(msg.fromMe).to.equal(true);
expect(msg.hasMedia).to.equal(true);
expect(msg.body).to.equal('here\'s my media');
expect(msg.to).to.equal(remoteId);
});
it('can send a media message as a document', async function() {
const media = new MessageMedia(
'image/png',
'iVBORw0KGgoAAAANSUhEUgAAAV4AAACWBAMAAABkyf1EAAAAG1BMVEXMzMyWlpacnJyqqqrFxcWxsbGjo6O3t7e+vr6He3KoAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEcElEQVR4nO2aTW/bRhCGh18ij1zKknMkbbf2UXITIEeyMhIfRaF1exQLA/JRclslRykO+rs7s7s0VwytNmhJtsA8gHZEcox9PTs7uysQgGEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmGYr2OWRK/ReIKI8Zt7Hb19wTcQ0uTkGh13bQupcw7gPOvdo12/5CzNtNR7xLUtNtT3CGBQ6g3InjY720pvofUec22LJPr8PhEp2OMPyI40PdwWUdronCu9yQpdPx53bQlfLKnfOVhlnDYRBXve4Ov+IZTeMgdedm0NR+xoXJeQvdJ3CvziykSukwil16W/Oe7aGjIjqc/9ib4jQlJy0uArtN4A0+cvXFvDkmUJ47sJ1Y1ATLDNVXZkNPIepQzxy1ki9fqiwbUj/I+64zxWNzyZnPuhvohJ9K70VvXBixpcu2SAHU+Xd9EKdEJDNpYP3AQr3bQSpPQ6Y6/4dl1z7ZDbArsszjA7L0g7ibB0CDcidUWVoErvIMKZh2Xs0LUzcLW6V5NfiUgNEbaYmAVL6bXl0nJRc+1S72ua/D/cTjGPlQj7eUqd7A096rYlRjdPYlhz7VIvxpVG3cemDKF+WAwLY/6XelOZKTXXzsC4xvDjjtSN6kHLhLke6PrwM8h1raf40qjrGO7H9aTEbduucjS04ZrYU/4iuS5Z2Hdt0rvCLFdmLEXcU30AGddST62o+sLcf5l6k7CP+ru4pLYqX/VFyxbm/utQbx/r22ZEbTb2f5I2kns1Y1OQR8ZyofX+TjJxj1Rz7QQVnf1QzR26Oth0ueJVYcRP6ZUPac/Rx/5M6ixO1dhSrT3Y1DpiYmx3tF4ZUdpz9LD/dSg9PXES0LB71BwcGjKROuV28lnvnv7HHJsezheBGH5+X2CfSfRbMKW+5aGs3JFjMrjGibJc0S7TJzqjHrh2hDybj9XRXNZa89Aro55XBdbW5wti2c/5WJ7jJ1RolVUn/HWpb0I58Tziup6Rx7Dm2hnbRP1GM9PW/NFmQ4PtVRVN63Wvxfmu5sowDMMwDMMwDMMwDMMwDMMwDMMwzL+CpT//F/6beoV8zb2Jmt4Qryx6lTUCsENQ75HOkhXAO3EPVgyQtKtUy3C/e+FJg17Zjnew1Xrdb9InbG4WqfUAftG+WhLwPVyfg536+MU7m4C1CMk4ZznpXZzDYI1PDL2nS1hpvc5cNd7E2sJg05Fe7/7d3Fln8Cvc3bwB616auxsKl4WPghjemHrDqyDWeu1UNW5s2btPnSQ75oOdunEwWazfwgVG0kqluYCM9OIjWOGnfA2b9G4Ha63XKpvQ8perTvTifJNhi6+WMWmi7smEZf6G8MmhlyGq+NqP8GV84TLuJr7UIQVx+bDEoEpRZIz42gs40OuN4Mv8hXzelV7KX1isH+ewTWckikyVv+CfHuqVF7I16gN0VKypX6wPsE+zFPzkinolU9UH8OMGvSpnZqKsv13p/RsMun6X5x/y2LeAr8O66lsBwzBMP/wJfyGq8pgBk6IAAAAASUVORK5CYII=',
'this is my filename.png'
);
const msg = await client.sendMessage(remoteId, media, { sendMediaAsDocument: true});
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.DOCUMENT);
expect(msg.fromMe).to.equal(true);
expect(msg.hasMedia).to.equal(true);
expect(msg.body).to.equal('this is my filename.png');
expect(msg.to).to.equal(remoteId);
});
it('can send a sticker message', async function() {
const media = new MessageMedia(
'image/png',
'iVBORw0KGgoAAAANSUhEUgAAAV4AAACWBAMAAABkyf1EAAAAG1BMVEXMzMyWlpacnJyqqqrFxcWxsbGjo6O3t7e+vr6He3KoAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEcElEQVR4nO2aTW/bRhCGh18ij1zKknMkbbf2UXITIEeyMhIfRaF1exQLA/JRclslRykO+rs7s7s0VwytNmhJtsA8gHZEcox9PTs7uysQgGEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmGYr2OWRK/ReIKI8Zt7Hb19wTcQ0uTkGh13bQupcw7gPOvdo12/5CzNtNR7xLUtNtT3CGBQ6g3InjY720pvofUec22LJPr8PhEp2OMPyI40PdwWUdronCu9yQpdPx53bQlfLKnfOVhlnDYRBXve4Ov+IZTeMgdedm0NR+xoXJeQvdJ3CvziykSukwil16W/Oe7aGjIjqc/9ib4jQlJy0uArtN4A0+cvXFvDkmUJ47sJ1Y1ATLDNVXZkNPIepQzxy1ki9fqiwbUj/I+64zxWNzyZnPuhvohJ9K70VvXBixpcu2SAHU+Xd9EKdEJDNpYP3AQr3bQSpPQ6Y6/4dl1z7ZDbArsszjA7L0g7ibB0CDcidUWVoErvIMKZh2Xs0LUzcLW6V5NfiUgNEbaYmAVL6bXl0nJRc+1S72ua/D/cTjGPlQj7eUqd7A096rYlRjdPYlhz7VIvxpVG3cemDKF+WAwLY/6XelOZKTXXzsC4xvDjjtSN6kHLhLke6PrwM8h1raf40qjrGO7H9aTEbduucjS04ZrYU/4iuS5Z2Hdt0rvCLFdmLEXcU30AGddST62o+sLcf5l6k7CP+ru4pLYqX/VFyxbm/utQbx/r22ZEbTb2f5I2kns1Y1OQR8ZyofX+TjJxj1Rz7QQVnf1QzR26Oth0ueJVYcRP6ZUPac/Rx/5M6ixO1dhSrT3Y1DpiYmx3tF4ZUdpz9LD/dSg9PXES0LB71BwcGjKROuV28lnvnv7HHJsezheBGH5+X2CfSfRbMKW+5aGs3JFjMrjGibJc0S7TJzqjHrh2hDybj9XRXNZa89Aro55XBdbW5wti2c/5WJ7jJ1RolVUn/HWpb0I58Tziup6Rx7Dm2hnbRP1GM9PW/NFmQ4PtVRVN63Wvxfmu5sowDMMwDMMwDMMwDMMwDMMwDMMwzL+CpT//F/6beoV8zb2Jmt4Qryx6lTUCsENQ75HOkhXAO3EPVgyQtKtUy3C/e+FJg17Zjnew1Xrdb9InbG4WqfUAftG+WhLwPVyfg536+MU7m4C1CMk4ZznpXZzDYI1PDL2nS1hpvc5cNd7E2sJg05Fe7/7d3Fln8Cvc3bwB616auxsKl4WPghjemHrDqyDWeu1UNW5s2btPnSQ75oOdunEwWazfwgVG0kqluYCM9OIjWOGnfA2b9G4Ha63XKpvQ8perTvTifJNhi6+WMWmi7smEZf6G8MmhlyGq+NqP8GV84TLuJr7UIQVx+bDEoEpRZIz42gs40OuN4Mv8hXzelV7KX1isH+ewTWckikyVv+CfHuqVF7I16gN0VKypX6wPsE+zFPzkinolU9UH8OMGvSpnZqKsv13p/RsMun6X5x/y2LeAr8O66lsBwzBMP/wJfyGq8pgBk6IAAAAASUVORK5CYII='
);
const msg = await client.sendMessage(remoteId, media, {sendMediaAsSticker: true});
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.STICKER);
expect(msg.fromMe).to.equal(true);
expect(msg.hasMedia).to.equal(true);
expect(msg.to).to.equal(remoteId);
});
it('can send a sticker message with custom author and name', async function() {
const media = new MessageMedia(
'image/png',
'iVBORw0KGgoAAAANSUhEUgAAAV4AAACWBAMAAABkyf1EAAAAG1BMVEXMzMyWlpacnJyqqqrFxcWxsbGjo6O3t7e+vr6He3KoAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEcElEQVR4nO2aTW/bRhCGh18ij1zKknMkbbf2UXITIEeyMhIfRaF1exQLA/JRclslRykO+rs7s7s0VwytNmhJtsA8gHZEcox9PTs7uysQgGEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmEYhmGYr2OWRK/ReIKI8Zt7Hb19wTcQ0uTkGh13bQupcw7gPOvdo12/5CzNtNR7xLUtNtT3CGBQ6g3InjY720pvofUec22LJPr8PhEp2OMPyI40PdwWUdronCu9yQpdPx53bQlfLKnfOVhlnDYRBXve4Ov+IZTeMgdedm0NR+xoXJeQvdJ3CvziykSukwil16W/Oe7aGjIjqc/9ib4jQlJy0uArtN4A0+cvXFvDkmUJ47sJ1Y1ATLDNVXZkNPIepQzxy1ki9fqiwbUj/I+64zxWNzyZnPuhvohJ9K70VvXBixpcu2SAHU+Xd9EKdEJDNpYP3AQr3bQSpPQ6Y6/4dl1z7ZDbArsszjA7L0g7ibB0CDcidUWVoErvIMKZh2Xs0LUzcLW6V5NfiUgNEbaYmAVL6bXl0nJRc+1S72ua/D/cTjGPlQj7eUqd7A096rYlRjdPYlhz7VIvxpVG3cemDKF+WAwLY/6XelOZKTXXzsC4xvDjjtSN6kHLhLke6PrwM8h1raf40qjrGO7H9aTEbduucjS04ZrYU/4iuS5Z2Hdt0rvCLFdmLEXcU30AGddST62o+sLcf5l6k7CP+ru4pLYqX/VFyxbm/utQbx/r22ZEbTb2f5I2kns1Y1OQR8ZyofX+TjJxj1Rz7QQVnf1QzR26Oth0ueJVYcRP6ZUPac/Rx/5M6ixO1dhSrT3Y1DpiYmx3tF4ZUdpz9LD/dSg9PXES0LB71BwcGjKROuV28lnvnv7HHJsezheBGH5+X2CfSfRbMKW+5aGs3JFjMrjGibJc0S7TJzqjHrh2hDybj9XRXNZa89Aro55XBdbW5wti2c/5WJ7jJ1RolVUn/HWpb0I58Tziup6Rx7Dm2hnbRP1GM9PW/NFmQ4PtVRVN63Wvxfmu5sowDMMwDMMwDMMwDMMwDMMwDMMwzL+CpT//F/6beoV8zb2Jmt4Qryx6lTUCsENQ75HOkhXAO3EPVgyQtKtUy3C/e+FJg17Zjnew1Xrdb9InbG4WqfUAftG+WhLwPVyfg536+MU7m4C1CMk4ZznpXZzDYI1PDL2nS1hpvc5cNd7E2sJg05Fe7/7d3Fln8Cvc3bwB616auxsKl4WPghjemHrDqyDWeu1UNW5s2btPnSQ75oOdunEwWazfwgVG0kqluYCM9OIjWOGnfA2b9G4Ha63XKpvQ8perTvTifJNhi6+WMWmi7smEZf6G8MmhlyGq+NqP8GV84TLuJr7UIQVx+bDEoEpRZIz42gs40OuN4Mv8hXzelV7KX1isH+ewTWckikyVv+CfHuqVF7I16gN0VKypX6wPsE+zFPzkinolU9UH8OMGvSpnZqKsv13p/RsMun6X5x/y2LeAr8O66lsBwzBMP/wJfyGq8pgBk6IAAAAASUVORK5CYII='
);
const msg = await client.sendMessage(remoteId, media, {
sendMediaAsSticker: true,
stickerAuthor: 'WWEBJS',
stickerName: 'My Sticker'
});
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.STICKER);
expect(msg.fromMe).to.equal(true);
expect(msg.hasMedia).to.equal(true);
expect(msg.to).to.equal(remoteId);
});
it('can send a location message', async function() {
const location = new Location(37.422, -122.084, 'Googleplex\nGoogle Headquarters');
const msg = await client.sendMessage(remoteId, location);
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.LOCATION);
expect(msg.fromMe).to.equal(true);
expect(msg.to).to.equal(remoteId);
expect(msg.location).to.be.instanceOf(Location);
expect(msg.location.latitude).to.equal(37.422);
expect(msg.location.longitude).to.equal(-122.084);
expect(msg.location.description).to.equal('Googleplex\nGoogle Headquarters');
});
it('can send a vCard as a contact card message', async function() {
const vCard = `BEGIN:VCARD
VERSION:3.0
FN;CHARSET=UTF-8:John Doe
N;CHARSET=UTF-8:Doe;John;;;
EMAIL;CHARSET=UTF-8;type=HOME,INTERNET:john@doe.com
TEL;TYPE=HOME,VOICE:1234567890
REV:2021-06-06T02:35:53.559Z
END:VCARD`;
const msg = await client.sendMessage(remoteId, vCard);
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.CONTACT_CARD);
expect(msg.fromMe).to.equal(true);
expect(msg.to).to.equal(remoteId);
expect(msg.body).to.equal(vCard);
expect(msg.vCards).to.have.lengthOf(1);
expect(msg.vCards[0]).to.equal(vCard);
});
it('can optionally turn off vCard parsing', async function() {
const vCard = `BEGIN:VCARD
VERSION:3.0
FN;CHARSET=UTF-8:John Doe
N;CHARSET=UTF-8:Doe;John;;;
EMAIL;CHARSET=UTF-8;type=HOME,INTERNET:john@doe.com
TEL;TYPE=HOME,VOICE:1234567890
REV:2021-06-06T02:35:53.559Z
END:VCARD`;
const msg = await client.sendMessage(remoteId, vCard, {parseVCards: false});
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.TEXT); // not a contact card
expect(msg.fromMe).to.equal(true);
expect(msg.to).to.equal(remoteId);
expect(msg.body).to.equal(vCard);
});
it('can send a Contact as a contact card message', async function() {
const contact = await client.getContactById(remoteId);
const msg = await client.sendMessage(remoteId, contact);
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.CONTACT_CARD);
expect(msg.fromMe).to.equal(true);
expect(msg.to).to.equal(remoteId);
expect(msg.body).to.match(/BEGIN:VCARD/);
expect(msg.vCards).to.have.lengthOf(1);
expect(msg.vCards[0]).to.match(/BEGIN:VCARD/);
});
it('can send multiple Contacts as a contact card message', async function () {
const contact1 = await client.getContactById(remoteId);
const contact2 = await client.getContactById('5511942167462@c.us'); //iFood
const msg = await client.sendMessage(remoteId, [contact1, contact2]);
expect(msg).to.be.instanceOf(Message);
expect(msg.type).to.equal(MessageTypes.CONTACT_CARD_MULTI);
expect(msg.fromMe).to.equal(true);
expect(msg.to).to.equal(remoteId);
expect(msg.vCards).to.have.lengthOf(2);
expect(msg.vCards[0]).to.match(/BEGIN:VCARD/);
expect(msg.vCards[1]).to.match(/BEGIN:VCARD/);
});
});
describe('Get Chats', function () {
it('can get a chat by its ID', async function () {
const chat = await client.getChatById(remoteId);
expect(chat).to.be.instanceOf(Chat);
expect(chat.id._serialized).to.eql(remoteId);
expect(chat.isGroup).to.eql(false);
});
it('can get all chats', async function () {
const chats = await client.getChats();
expect(chats.length).to.be.greaterThanOrEqual(1);
const chat = chats.find(c => c.id._serialized === remoteId);
expect(chat).to.exist;
expect(chat).to.be.instanceOf(Chat);
});
});
describe('Get Contacts', function () {
it('can get a contact by its ID', async function () {
const contact = await client.getContactById(remoteId);
expect(contact).to.be.instanceOf(Contact);
expect(contact.id._serialized).to.eql(remoteId);
expect(contact.number).to.eql(remoteId.split('@')[0]);
});
it('can get all contacts', async function () {
const contacts = await client.getContacts();
expect(contacts.length).to.be.greaterThanOrEqual(1);
const contact = contacts.find(c => c.id._serialized === remoteId);
expect(contact).to.exist;
expect(contact).to.be.instanceOf(Contact);
});
});
describe('Numbers and Users', function () {
it('can verify that a user is registered', async function () {
const isRegistered = await client.isRegisteredUser(remoteId);
expect(isRegistered).to.be.true;
});
it('can verify that a user is not registered', async function () {
const isRegistered = await client.isRegisteredUser('9999999999@c.us');
expect(isRegistered).to.be.false;
});
it('can get a number\'s whatsapp id', async function () {
const number = remoteId.split('@')[0];
const numberId = await client.getNumberId(number);
expect(numberId).to.eql({
server: 'c.us',
user: number,
_serialized: `${number}@c.us`
});
});
it('returns null when getting an unregistered number\'s whatsapp id', async function () {
const number = '9999999999';
const numberId = await client.getNumberId(number);
expect(numberId).to.eql(null);
});
});
});
});

40
tests/helper.js Normal file
View File

@@ -0,0 +1,40 @@
const path = require('path');
const Client = require('../src/Client');
const Util = require('../src/util/Util');
require('dotenv').config();
const remoteId = process.env.WWEBJS_TEST_REMOTE_ID;
if(!remoteId) throw new Error('The WWEBJS_TEST_REMOTE_ID environment variable has not been set.');
function getSessionFromEnv() {
const envSession = process.env.WWEBJS_TEST_SESSION;
if(envSession) return JSON.parse(envSession);
const envSessionPath = process.env.WWEBJS_TEST_SESSION_PATH;
if(envSessionPath) {
const absPath = path.resolve(process.cwd(), envSessionPath);
return require(absPath);
}
throw new Error('No session found in environment.');
}
function createClient({withSession, options: additionalOpts}={}) {
const options = {};
if(withSession) {
options.session = getSessionFromEnv();
}
return new Client(Util.mergeDefault(options, additionalOpts || {}));
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
module.exports = {
sleep,
createClient,
remoteId
};

View File

@@ -0,0 +1 @@
2.2126.10

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env node
const fs = require('fs');
const puppeteer = require('puppeteer');
const { DefaultOptions } = require('../../src/util/Constants');
const getLatestVersion = async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setUserAgent(DefaultOptions.userAgent);
await page.goto('https://web.whatsapp.com/', { waitUntil: 'load'});
await page.waitForSelector('.landing-header');
const version = await page.evaluate(() => window.Debug.VERSION);
await browser.close();
return version;
};
const getCurrentVersion = () => {
try {
const versionFile = fs.readFileSync('./.version');
return versionFile ? versionFile.toString() : null;
} catch(_) {
return null;
}
};
const updateVersion = async (oldVersion, newVersion) => {
const readmePath = '../../README.md';
const readme = fs.readFileSync(readmePath);
const newReadme = readme.toString().replaceAll(oldVersion, newVersion);
fs.writeFileSync(readmePath, newReadme);
fs.writeFileSync('./.version', newVersion);
};
(async () => {
const currentVersion = getCurrentVersion();
const version = await getLatestVersion();
console.log(`Current version: ${currentVersion}`);
console.log(`Latest version: ${version}`);
if(currentVersion !== version) {
console.log('Updating files...');
await updateVersion(currentVersion, version);
console.log('Updated!');
} else {
console.log('No changes.');
}
})();