mirror of
https://github.com/cheveguerra/whatsapp-web.js.git
synced 2026-04-19 20:19:14 +00:00
feat: Multi-device support (#889)
* 🚑 Added ready selector for multi-device * SendMessage fix * File management system and some fixes * cleanup * cleanup again * eslint * critical fix for reloading the same session * Checking for valid folder name (regex) * ESLint hotfix (regex escapes) * Typings cleanup * cleanup listener * Multi-device Branch merge (#888) * Duplicate * qr fix and allow non-beta users to connect * urgent: selector fix * urgent: qr timeout fix * fix * Updated type so no TS error when sending list/buttons * Update index.d.ts * fix QueryExist for Multidevice (#928) * creates isRegisteredUserBeta * fix QueryExist * fix Error: GROUP_JID: invalid jid type: Not an instance of WID issue (#926) * fix Error: GROUP_JID: invalid jid type: Not an instance of WID issue * clean code * Cleanup * Fix for update chrome error * ESLint fix * :red_light: fix for RMDIR * Update README.md * Update README.md * fix: getProfilePicUrl fix by victormga (#941) * fix: MD presence available/unavailable (#942) * delete session when appropriate & fix for SW * ignore QR timeout errors * Presence and ChatState updates working for MD+Non-MD * shell uses new session storage * lint fix * support session.json-based auth for non-md * md fix * md fix * fix shell clientId * remove exclusive mocha test * make linkPreview default to false * remove ignored errors on getQuotedMessage * fix: dont modify existing this.options.puppeteer object * tests work with new dir auth * remove exclusive test * fixes and tests for group creation and participant functions * remove unused function * wip fix group settings functions * isRegisteredUser && getNumberId hotFix (#955) * isRegisteredUser && getNumberId hotFix A fix for client.isRegisteredUser and client.getNumberId. Use for reference or if you are stuck with MD and NEEDS this function. Problably Whatsapp will break this in a couple weeks * fix for non-md Co-authored-by: Rajeh Taher <rajeh@reforward.dev> * Fix WA 2.2146.9 MD + victormga branch (#991) * qrcode now uses observers instead of timeout * automatic auth/qrcode detection * Fix WA 2.2146.9 MD Got from github:victormga/whatsapp-web.js#multidevice maybe it's behind pedro branch Co-authored-by: victormga <victor_mga@hotmail.com> * fix * fix* * getnumberid to multidevice (#1027) * getNumberId to main isRegisteredUser && getNumberId hotFix #955 To main * Update Client.js Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com> * Update Client.js * Message.raw() (#1005) * Message.raw() * i just noticed * Update index.d.ts * Update index.d.ts * Update Message.js * Get rid of sharp now!!!!!!!! (#1045) * commit 1 * finally, gotten rid of sharp * pckg.json * service worker fix & disableMessage option * typings * Update example.js * clear session system * Update Client.js * Update Client.js * Fix accepting group private invite (#1094) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * [MD] Add getCommonGroups with specific user. (#1097) * Add getCommonGroups with specific user. * Fix * Fix * Fix Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Fix getCommonGroups. (#1122) * Fix of Unexpected identifier async destroy() (#1123) * Fix of Unexpected identifier async destroy() * Fix made in #1107 * Temporary fix for "Sticker" module * some really quick changes * Update Injected.js * Update Injected.js * Update index.d.ts * fix: getNumberId Solved (#1142) * getNumberId Solved * isRegisteredUser Solved * formmated * Apply suggestions from code review * Update src/util/Injected.js Co-authored-by: Rajeh Taher <rajeh@reforward.dev> * Fix: "Chrome user data dir was not found ..." fixes the error caused by puppeteer. * Update Client.js (#1154) * fix: getNumberId and isRegisteredUser (#1159) * fix: getNumberId and isRegisteredUser * Apply suggestions from code review Co-authored-by: Rajeh Taher <rajeh@reforward.dev> * Update client.js * Update Injected.js * Update Client.js * Update index.d.ts * Update Client.js * Update Client.js * fix lint indentation * fix auth_failure event for non-md, tests * fix setting group subject * fix finding Label module * set remember-me after clearing localStorage * fix: send messages to groups correctly on MD, use new ID format * fix setting / getting contact status * fix msg.getInfo, add message tests * fix group settings functions * fix set group description, handle errors in setSubject * fix group invite functions * fix leaving group * bring back phone info for non-md users * remove unused option, update typings * add back jsdoc for qr event * fix setting sticker metadata, clean up sticker functions * rawData is a get only property * fix and simplify getNumberId/isRegisteredUser * fix getInviteInfo * setDisplayName returns bool, not yet implemented for md * fix: stream module (#1241) * linkPreview has no effect on MD, return default to true * fix: del linkPreview option on md * cleanup, types and docs updates * update readmes / test notes * remove DS_Store * DS_Store in gitignore * test stability (timeouts/sleeps) Co-authored-by: Rajeh Taher <rajeh@reforward.tk> Co-authored-by: Gustavo B <52040719+Gugabit@users.noreply.github.com> Co-authored-by: Maikel Ortega Hernández <maikeloh@gmail.com> Co-authored-by: victormga <victor_mga@hotmail.com> Co-authored-by: Pedro Lopez <pedroslopez@me.com> Co-authored-by: tuyuribr <45042245+tuyuribr@users.noreply.github.com> Co-authored-by: gon <68490103+nekiak@users.noreply.github.com> Co-authored-by: Alon Schwartzblat <63599777+Schwartzblat@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Šebestíček <44745014+SebestikCZ@users.noreply.github.com> Co-authored-by: Emmanuel Anaya Luna <38712443+KeruMx@users.noreply.github.com> Co-authored-by: L337C0D3R <51872799+L337C0D3R@users.noreply.github.com> Co-authored-by: Reni Delonzek <renidelonzek@gmail.com>
This commit is contained in:
@@ -3,8 +3,12 @@
|
||||
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_SESSION`: A JSON-formatted string with legacy auth session details. Must include `WABrowserId`, `WASecretBundle`, `WAToken1` and `WAToken2`.
|
||||
- `WWEBJS_TEST_SESSION_PATH`: Path to a JSON file that contains the legacy auth session details. Must include `WABrowserId`, `WASecretBundle`, `WAToken1` and `WAToken2`.
|
||||
- `WWEBJS_TEST_CLIENT_ID`: `clientId` to use for local file based authentication.
|
||||
- `WWEBJS_TEST_REMOTE_ID`: A valid WhatsApp ID that you can send messages to, e.g. `123456789@c.us`. It should be different from the ID used by the provided session.
|
||||
|
||||
You *must* set `WWEBJS_TEST_REMOTE_ID` **and** either `WWEBJS_TEST_SESSION` or `WWEBJS_TEST_SESSION_PATH` for the tests to run properly.
|
||||
You *must* set `WWEBJS_TEST_REMOTE_ID` **and** either `WWEBJS_TEST_SESSION`, `WWEBJS_TEST_SESSION_PATH` or `WWEBJS_TEST_CLIENT_ID` for the tests to run properly.
|
||||
|
||||
### Multidevice
|
||||
Some of the tested functionality depends on whether the account has multidevice enabled or not. If you are using multidevice, you should set `WWEBJS_TEST_MD=1`.
|
||||
277
tests/client.js
277
tests/client.js
@@ -1,4 +1,5 @@
|
||||
const {expect} = require('chai');
|
||||
const chai = require('chai');
|
||||
const chaiAsPromised = require('chai-as-promised');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const helper = require('./helper');
|
||||
@@ -9,7 +10,11 @@ const MessageMedia = require('../src/structures/MessageMedia');
|
||||
const Location = require('../src/structures/Location');
|
||||
const { MessageTypes, WAState } = require('../src/util/Constants');
|
||||
|
||||
const expect = chai.expect;
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
const remoteId = helper.remoteId;
|
||||
const isMD = helper.isMD();
|
||||
|
||||
describe('Client', function() {
|
||||
describe('Authentication', function() {
|
||||
@@ -47,76 +52,6 @@ describe('Client', function() {
|
||||
expect(disconnectedCallback.calledOnceWith('Max qrcode retries reached')).to.eql(true);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
@@ -124,7 +59,10 @@ describe('Client', function() {
|
||||
const qrCallback = sinon.spy();
|
||||
const readyCallback = sinon.spy();
|
||||
|
||||
const client = helper.createClient({withSession: true});
|
||||
const client = helper.createClient({
|
||||
authenticated: true,
|
||||
});
|
||||
|
||||
client.on('qr', qrCallback);
|
||||
client.on('authenticated', authenticatedCallback);
|
||||
client.on('ready', readyCallback);
|
||||
@@ -132,59 +70,137 @@ describe('Client', function() {
|
||||
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);
|
||||
|
||||
if(helper.isUsingDeprecatedSession()) {
|
||||
const newSession = authenticatedCallback.args[0][0];
|
||||
expect(newSession).to.have.key([
|
||||
'WABrowserId',
|
||||
'WASecretBundle',
|
||||
'WAToken1',
|
||||
'WAToken2'
|
||||
]);
|
||||
}
|
||||
|
||||
expect(readyCallback.called).to.equal(true);
|
||||
expect(qrCallback.called).to.equal(false);
|
||||
|
||||
await client.destroy();
|
||||
});
|
||||
|
||||
it('can take over if client was logged in somewhere else with takeoverOnConflict=true', async function() {
|
||||
this.timeout(40000);
|
||||
|
||||
const readyCallback1 = sinon.spy();
|
||||
const readyCallback2 = sinon.spy();
|
||||
const disconnectedCallback1 = sinon.spy();
|
||||
const disconnectedCallback2 = sinon.spy();
|
||||
|
||||
const client1 = helper.createClient({
|
||||
withSession: true,
|
||||
options: { takeoverOnConflict: true, takeoverTimeoutMs: 5000 }
|
||||
});
|
||||
const client2 = helper.createClient({withSession: true});
|
||||
|
||||
client1.on('ready', readyCallback1);
|
||||
client2.on('ready', readyCallback2);
|
||||
client1.on('disconnected', disconnectedCallback1);
|
||||
client2.on('disconnected', disconnectedCallback2);
|
||||
|
||||
await client1.initialize();
|
||||
expect(readyCallback1.called).to.equal(true);
|
||||
expect(readyCallback2.called).to.equal(false);
|
||||
expect(disconnectedCallback1.called).to.equal(false);
|
||||
expect(disconnectedCallback2.called).to.equal(false);
|
||||
|
||||
await client2.initialize();
|
||||
expect(readyCallback2.called).to.equal(true);
|
||||
expect(disconnectedCallback1.called).to.equal(false);
|
||||
expect(disconnectedCallback2.called).to.equal(false);
|
||||
|
||||
// wait for takeoverTimeoutMs to kick in
|
||||
await helper.sleep(5200);
|
||||
expect(disconnectedCallback1.called).to.equal(false);
|
||||
expect(disconnectedCallback2.called).to.equal(true);
|
||||
expect(disconnectedCallback2.calledWith(WAState.CONFLICT)).to.equal(true);
|
||||
|
||||
await client1.destroy();
|
||||
|
||||
});
|
||||
|
||||
describe('Non-MD only', function () {
|
||||
if(!isMD) {
|
||||
it('can take over if client was logged in somewhere else with takeoverOnConflict=true', async function() {
|
||||
this.timeout(40000);
|
||||
|
||||
const readyCallback1 = sinon.spy();
|
||||
const readyCallback2 = sinon.spy();
|
||||
const disconnectedCallback1 = sinon.spy();
|
||||
const disconnectedCallback2 = sinon.spy();
|
||||
|
||||
const client1 = helper.createClient({
|
||||
authenticated: true,
|
||||
options: { takeoverOnConflict: true, takeoverTimeoutMs: 5000 }
|
||||
});
|
||||
const client2 = helper.createClient({authenticated: true});
|
||||
|
||||
client1.on('ready', readyCallback1);
|
||||
client2.on('ready', readyCallback2);
|
||||
client1.on('disconnected', disconnectedCallback1);
|
||||
client2.on('disconnected', disconnectedCallback2);
|
||||
|
||||
await client1.initialize();
|
||||
expect(readyCallback1.called).to.equal(true);
|
||||
expect(readyCallback2.called).to.equal(false);
|
||||
expect(disconnectedCallback1.called).to.equal(false);
|
||||
expect(disconnectedCallback2.called).to.equal(false);
|
||||
|
||||
await client2.initialize();
|
||||
expect(readyCallback2.called).to.equal(true);
|
||||
expect(disconnectedCallback1.called).to.equal(false);
|
||||
expect(disconnectedCallback2.called).to.equal(false);
|
||||
|
||||
// wait for takeoverTimeoutMs to kick in
|
||||
await helper.sleep(5200);
|
||||
expect(disconnectedCallback1.called).to.equal(false);
|
||||
expect(disconnectedCallback2.called).to.equal(true);
|
||||
expect(disconnectedCallback2.calledWith(WAState.CONFLICT)).to.equal(true);
|
||||
|
||||
await client1.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,
|
||||
useDeprecatedSessionAuth: true
|
||||
}
|
||||
});
|
||||
|
||||
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,
|
||||
useDeprecatedSessionAuth: 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();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Authenticated', function() {
|
||||
@@ -192,7 +208,7 @@ describe('Client', function() {
|
||||
|
||||
before(async function() {
|
||||
this.timeout(35000);
|
||||
client = helper.createClient({withSession: true});
|
||||
client = helper.createClient({authenticated: true});
|
||||
await client.initialize();
|
||||
});
|
||||
|
||||
@@ -221,27 +237,38 @@ describe('Client', function() {
|
||||
'BlockContact',
|
||||
'Call',
|
||||
'Chat',
|
||||
'ChatState',
|
||||
'Cmd',
|
||||
'Conn',
|
||||
'Contact',
|
||||
'DownloadManager',
|
||||
'Features',
|
||||
'GroupMetadata',
|
||||
'GroupParticipants',
|
||||
'GroupUtils',
|
||||
'Invite',
|
||||
'InviteInfo',
|
||||
'JoinInviteV4',
|
||||
'Label',
|
||||
'MediaObject',
|
||||
'MediaPrep',
|
||||
'MediaTypes',
|
||||
'MediaUpload',
|
||||
'MessageInfo',
|
||||
'Msg',
|
||||
'MsgKey',
|
||||
'OpaqueData',
|
||||
'QueryOrder',
|
||||
'QueryProduct',
|
||||
'PresenceUtils',
|
||||
'QueryExist',
|
||||
'QueryProduct',
|
||||
'QueryOrder',
|
||||
'SendClear',
|
||||
'SendDelete',
|
||||
'SendMessage',
|
||||
'SendSeen',
|
||||
'StatusUtils',
|
||||
'Sticker',
|
||||
'UploadUtils',
|
||||
'UserConstructor',
|
||||
@@ -249,7 +276,9 @@ describe('Client', function() {
|
||||
'Validators',
|
||||
'Wap',
|
||||
'WidFactory',
|
||||
'genId'
|
||||
'findCommonGroups',
|
||||
'genId',
|
||||
'getProfilePicFull',
|
||||
];
|
||||
|
||||
const loadedModules = await client.pupPage.evaluate((expectedModules) => {
|
||||
@@ -535,8 +564,6 @@ END:VCARD`;
|
||||
|
||||
describe('Search messages', function () {
|
||||
it('can search for messages', async function () {
|
||||
this.timeout(5000);
|
||||
|
||||
const m1 = await client.sendMessage(remoteId, 'I\'m searching for Super Mario Brothers');
|
||||
const m2 = await client.sendMessage(remoteId, 'This also contains Mario');
|
||||
const m3 = await client.sendMessage(remoteId, 'Nothing of interest here, just Luigi');
|
||||
@@ -581,4 +608,4 @@ END:VCARD`;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
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 isUsingDeprecatedSession() {
|
||||
return Boolean(process.env.WWEBJS_TEST_SESSION || process.env.WWEBJS_TEST_SESSION_PATH);
|
||||
}
|
||||
|
||||
function isMD() {
|
||||
return Boolean(process.env.WWEBJS_TEST_MD);
|
||||
}
|
||||
|
||||
if(isUsingDeprecatedSession() && isMD()) throw 'Cannot use deprecated sessions with WWEBJS_TEST_MD=true';
|
||||
|
||||
function getSessionFromEnv() {
|
||||
if (!isUsingDeprecatedSession()) return null;
|
||||
|
||||
const envSession = process.env.WWEBJS_TEST_SESSION;
|
||||
if(envSession) return JSON.parse(envSession);
|
||||
|
||||
@@ -16,17 +28,27 @@ function getSessionFromEnv() {
|
||||
const absPath = path.resolve(process.cwd(), envSessionPath);
|
||||
return require(absPath);
|
||||
}
|
||||
|
||||
throw new Error('No session found in environment.');
|
||||
}
|
||||
|
||||
function createClient({withSession, options: additionalOpts}={}) {
|
||||
function createClient({authenticated, options: additionalOpts}={}) {
|
||||
const options = {};
|
||||
if(withSession) {
|
||||
options.session = getSessionFromEnv();
|
||||
|
||||
if(authenticated) {
|
||||
const deprecatedSession = getSessionFromEnv();
|
||||
if(deprecatedSession) {
|
||||
options.session = deprecatedSession;
|
||||
options.useDeprecatedSessionAuth = true;
|
||||
} else {
|
||||
const clientId = process.env.WWEBJS_TEST_CLIENT_ID;
|
||||
if(!clientId) throw new Error('No session found in environment.');
|
||||
options.clientId = clientId;
|
||||
}
|
||||
} else {
|
||||
options.clientId = crypto.randomBytes(5).toString('hex');
|
||||
}
|
||||
|
||||
return new Client(Util.mergeDefault(options, additionalOpts || {}));
|
||||
const allOpts = {...options, ...(additionalOpts || {})};
|
||||
return new Client(allOpts);
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
@@ -36,5 +58,7 @@ function sleep(ms) {
|
||||
module.exports = {
|
||||
sleep,
|
||||
createClient,
|
||||
remoteId
|
||||
isUsingDeprecatedSession,
|
||||
isMD,
|
||||
remoteId,
|
||||
};
|
||||
@@ -13,7 +13,7 @@ describe('Chat', function () {
|
||||
|
||||
before(async function() {
|
||||
this.timeout(35000);
|
||||
client = helper.createClient({ withSession: true });
|
||||
client = helper.createClient({ authenticated: true });
|
||||
await client.initialize();
|
||||
chat = await client.getChatById(remoteId);
|
||||
});
|
||||
@@ -32,9 +32,9 @@ describe('Chat', function () {
|
||||
});
|
||||
|
||||
it('can fetch messages sent in a chat', async function () {
|
||||
this.timeout(5000);
|
||||
await helper.sleep(1000);
|
||||
const msg = await chat.sendMessage('another message');
|
||||
await helper.sleep(500);
|
||||
|
||||
const messages = await chat.fetchMessages();
|
||||
expect(messages.length).to.be.greaterThanOrEqual(2);
|
||||
@@ -49,6 +49,7 @@ describe('Chat', function () {
|
||||
it('can use a limit when fetching messages sent in a chat', async function () {
|
||||
await helper.sleep(1000);
|
||||
const msg = await chat.sendMessage('yet another message');
|
||||
await helper.sleep(500);
|
||||
|
||||
const messages = await chat.fetchMessages({limit: 1});
|
||||
expect(messages).to.have.lengthOf(1);
|
||||
@@ -80,6 +81,8 @@ describe('Chat', function () {
|
||||
const res = await chat.sendSeen();
|
||||
expect(res).to.equal(true);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// refresh chat
|
||||
chat = await client.getChatById(remoteId);
|
||||
expect(chat.unreadCount).to.equal(0);
|
||||
@@ -137,6 +140,8 @@ describe('Chat', function () {
|
||||
it('can mute a chat forever', async function() {
|
||||
await chat.mute();
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// refresh chat
|
||||
chat = await client.getChatById(remoteId);
|
||||
expect(chat.isMuted).to.equal(true);
|
||||
@@ -147,6 +152,8 @@ describe('Chat', function () {
|
||||
const unmuteDate = new Date(new Date().getTime() + (1000*60*60));
|
||||
await chat.mute(unmuteDate);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// refresh chat
|
||||
chat = await client.getChatById(remoteId);
|
||||
expect(chat.isMuted).to.equal(true);
|
||||
@@ -168,9 +175,7 @@ describe('Chat', function () {
|
||||
|
||||
// eslint-disable-next-line mocha/no-skipped-tests
|
||||
describe.skip('Destructive operations', function () {
|
||||
it('can clear all messages from chat', async function () {
|
||||
this.timeout(5000);
|
||||
|
||||
it('can clear all messages from chat', async function () {
|
||||
const res = await chat.clearMessages();
|
||||
expect(res).to.equal(true);
|
||||
|
||||
|
||||
227
tests/structures/group.js
Normal file
227
tests/structures/group.js
Normal file
@@ -0,0 +1,227 @@
|
||||
const { expect } = require('chai');
|
||||
const helper = require('../helper');
|
||||
|
||||
const remoteId = helper.remoteId;
|
||||
|
||||
describe('Group', function() {
|
||||
let client;
|
||||
let group;
|
||||
|
||||
before(async function() {
|
||||
this.timeout(35000);
|
||||
client = helper.createClient({
|
||||
authenticated: true,
|
||||
});
|
||||
await client.initialize();
|
||||
|
||||
const createRes = await client.createGroup('My Awesome Group', [remoteId]);
|
||||
expect(createRes.gid).to.exist;
|
||||
await helper.sleep(500);
|
||||
group = await client.getChatById(createRes.gid._serialized);
|
||||
expect(group).to.exist;
|
||||
});
|
||||
|
||||
beforeEach(async function () {
|
||||
await helper.sleep(500);
|
||||
});
|
||||
|
||||
describe('Settings', function () {
|
||||
it('can change the group subject', async function () {
|
||||
expect(group.name).to.equal('My Awesome Group');
|
||||
const res = await group.setSubject('My Amazing Group');
|
||||
expect(res).to.equal(true);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.name).to.equal('My Amazing Group');
|
||||
});
|
||||
|
||||
it('can change the group description', async function () {
|
||||
expect(group.description).to.equal(undefined);
|
||||
const res = await group.setDescription('some description');
|
||||
expect(res).to.equal(true);
|
||||
expect(group.description).to.equal('some description');
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.description).to.equal('some description');
|
||||
});
|
||||
|
||||
it('can set only admins able to send messages', async function () {
|
||||
expect(group.groupMetadata.announce).to.equal(false);
|
||||
const res = await group.setMessagesAdminsOnly();
|
||||
expect(res).to.equal(true);
|
||||
expect(group.groupMetadata.announce).to.equal(true);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.groupMetadata.announce).to.equal(true);
|
||||
});
|
||||
|
||||
it('can set all participants able to send messages', async function () {
|
||||
expect(group.groupMetadata.announce).to.equal(true);
|
||||
const res = await group.setMessagesAdminsOnly(false);
|
||||
expect(res).to.equal(true);
|
||||
expect(group.groupMetadata.announce).to.equal(false);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.groupMetadata.announce).to.equal(false);
|
||||
});
|
||||
|
||||
it('can set only admins able to set group info', async function () {
|
||||
expect(group.groupMetadata.restrict).to.equal(false);
|
||||
const res = await group.setInfoAdminsOnly();
|
||||
expect(res).to.equal(true);
|
||||
expect(group.groupMetadata.restrict).to.equal(true);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.groupMetadata.restrict).to.equal(true);
|
||||
});
|
||||
|
||||
it('can set all participants able to set group info', async function () {
|
||||
expect(group.groupMetadata.restrict).to.equal(true);
|
||||
const res = await group.setInfoAdminsOnly(false);
|
||||
expect(res).to.equal(true);
|
||||
expect(group.groupMetadata.restrict).to.equal(false);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.groupMetadata.restrict).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invites', function () {
|
||||
it('can get the invite code', async function () {
|
||||
const code = await group.getInviteCode();
|
||||
expect(typeof code).to.equal('string');
|
||||
});
|
||||
|
||||
it('can get invite info', async function () {
|
||||
const code = await group.getInviteCode();
|
||||
const info = await client.getInviteInfo(code);
|
||||
expect(info.id._serialized).to.equal(group.id._serialized);
|
||||
expect(info.participants.length).to.equal(2);
|
||||
});
|
||||
|
||||
it('can revoke the invite code', async function () {
|
||||
const code = await group.getInviteCode();
|
||||
const newCode = await group.revokeInvite();
|
||||
expect(typeof newCode).to.equal('string');
|
||||
expect(newCode).to.not.equal(code);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Participants', function () {
|
||||
it('can promote a user to admin', async function () {
|
||||
let participant = group.participants.find(p => p.id._serialized === remoteId);
|
||||
expect(participant).to.exist;
|
||||
expect(participant.isAdmin).to.equal(false);
|
||||
|
||||
const res = await group.promoteParticipants([remoteId]);
|
||||
expect(res.status).to.be.greaterThanOrEqual(200);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload and check
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.exist;
|
||||
expect(participant.isAdmin).to.equal(true);
|
||||
});
|
||||
|
||||
it('can demote a user', async function () {
|
||||
let participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.exist;
|
||||
expect(participant.isAdmin).to.equal(true);
|
||||
|
||||
const res = await group.demoteParticipants([remoteId]);
|
||||
expect(res.status).to.be.greaterThanOrEqual(200);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload and check
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.exist;
|
||||
expect(participant.isAdmin).to.equal(false);
|
||||
});
|
||||
|
||||
it('can remove a user from the group', async function () {
|
||||
let participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.exist;
|
||||
|
||||
const res = await group.removeParticipants([remoteId]);
|
||||
expect(res.status).to.be.greaterThanOrEqual(200);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload and check
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.not.exist;
|
||||
});
|
||||
|
||||
it('can add back a user to the group', async function () {
|
||||
let participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.not.exist;
|
||||
|
||||
const res = await group.addParticipants([remoteId]);
|
||||
expect(res.status).to.be.greaterThanOrEqual(200);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload and check
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
participant = group.participants.find(p => p.id._serialized=== remoteId);
|
||||
expect(participant).to.exist;
|
||||
});
|
||||
});
|
||||
|
||||
describe('Leave / re-join', function () {
|
||||
let code;
|
||||
before(async function () {
|
||||
code = await group.getInviteCode();
|
||||
});
|
||||
|
||||
it('can leave the group', async function () {
|
||||
expect(group.isReadOnly).to.equal(false);
|
||||
await group.leave();
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload and check
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.isReadOnly).to.equal(true);
|
||||
});
|
||||
|
||||
it('can join a group via invite code', async function () {
|
||||
const chatId = await client.acceptInvite(code);
|
||||
expect(chatId).to.equal(group.id._serialized);
|
||||
|
||||
await helper.sleep(1000);
|
||||
|
||||
// reload and check
|
||||
group = await client.getChatById(group.id._serialized);
|
||||
expect(group.isReadOnly).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await client.destroy();
|
||||
});
|
||||
|
||||
});
|
||||
112
tests/structures/message.js
Normal file
112
tests/structures/message.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const { expect } = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const helper = require('../helper');
|
||||
const { Contact, Chat } = require('../../src/structures');
|
||||
|
||||
const remoteId = helper.remoteId;
|
||||
|
||||
describe('Message', function () {
|
||||
let client;
|
||||
let chat;
|
||||
let message;
|
||||
|
||||
before(async function() {
|
||||
this.timeout(35000);
|
||||
client = helper.createClient({ authenticated: true });
|
||||
await client.initialize();
|
||||
|
||||
chat = await client.getChatById(remoteId);
|
||||
message = await chat.sendMessage('this is only a test');
|
||||
|
||||
// wait for message to be sent
|
||||
await helper.sleep(1000);
|
||||
});
|
||||
|
||||
after(async function () {
|
||||
await client.destroy();
|
||||
});
|
||||
|
||||
it('can get the related chat', async function () {
|
||||
const chat = await message.getChat();
|
||||
expect(chat).to.be.instanceOf(Chat);
|
||||
expect(chat.id._serialized).to.equal(remoteId);
|
||||
});
|
||||
|
||||
it('can get the related contact', async function () {
|
||||
const contact = await message.getContact();
|
||||
expect(contact).to.be.instanceOf(Contact);
|
||||
expect(contact.id._serialized).to.equal(client.info.wid._serialized);
|
||||
});
|
||||
|
||||
it('can get message info', async function () {
|
||||
const info = await message.getInfo();
|
||||
expect(typeof info).to.equal('object');
|
||||
expect(Array.isArray(info.played)).to.equal(true);
|
||||
expect(Array.isArray(info.read)).to.equal(true);
|
||||
expect(Array.isArray(info.delivery)).to.equal(true);
|
||||
});
|
||||
|
||||
describe('Replies', function () {
|
||||
let replyMsg;
|
||||
|
||||
it('can reply to a message', async function () {
|
||||
replyMsg = await message.reply('this is my reply');
|
||||
expect(replyMsg.hasQuotedMsg).to.equal(true);
|
||||
});
|
||||
|
||||
it('can get the quoted message', async function () {
|
||||
const quotedMsg = await replyMsg.getQuotedMessage();
|
||||
expect(quotedMsg.id._serialized).to.equal(message.id._serialized);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Star', function () {
|
||||
it('can star a message', async function () {
|
||||
expect(message.isStarred).to.equal(false);
|
||||
await message.star();
|
||||
|
||||
// reload and check
|
||||
await message.reload();
|
||||
expect(message.isStarred).to.equal(true);
|
||||
});
|
||||
|
||||
it('can un-star a message', async function () {
|
||||
expect(message.isStarred).to.equal(true);
|
||||
await message.unstar();
|
||||
|
||||
// reload and check
|
||||
await message.reload();
|
||||
expect(message.isStarred).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Delete', function () {
|
||||
it('can delete a message for me', async function () {
|
||||
await message.delete();
|
||||
|
||||
await helper.sleep(1000);
|
||||
expect(await message.reload()).to.equal(null);
|
||||
});
|
||||
|
||||
it('can delete a message for everyone', async function () {
|
||||
message = await chat.sendMessage('sneaky message');
|
||||
await helper.sleep(1000);
|
||||
|
||||
const callback = sinon.spy();
|
||||
client.once('message_revoke_everyone', callback);
|
||||
|
||||
await message.delete(true);
|
||||
await helper.sleep(1000);
|
||||
|
||||
expect(await message.reload()).to.equal(null);
|
||||
expect(callback.called).to.equal(true);
|
||||
const [ revokeMsg, originalMsg ] = callback.args[0];
|
||||
expect(revokeMsg.id._serialized).to.equal(originalMsg.id._serialized);
|
||||
expect(originalMsg.body).to.equal('sneaky message');
|
||||
expect(originalMsg.type).to.equal('chat');
|
||||
expect(revokeMsg.body).to.equal('');
|
||||
expect(revokeMsg.type).to.equal('revoked');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user