From b3dfe65cbbed2340779d029ee6fbcbf7baf3d04c Mon Sep 17 00:00:00 2001 From: Pierrick Date: Tue, 14 Jun 2016 15:30:58 +0200 Subject: [PATCH 1/6] Get card limits Create Card class. `Account.cards()` return instances of `Card` Add `Account.getCard()`, `Card.limits()` & `Account.cardLimits()` --- lib/account.js | 35 ++++++++++------- lib/api.js | 22 +++++++++++ lib/card.js | 41 ++++++++++++++++++++ tests/account/account.js | 2 +- tests/account/addresses.js | 2 +- tests/account/auth.js | 4 +- tests/account/barzahlen.js | 2 +- tests/account/cards.js | 73 +++++++++++++++++++++-------------- tests/account/contacts.js | 2 +- tests/account/invitations.js | 8 ++-- tests/account/limits.js | 6 +-- tests/account/me.js | 4 +- tests/account/memo.js | 8 ++-- tests/account/recipients.js | 2 +- tests/account/statement.js | 4 +- tests/account/statements.js | 2 +- tests/account/statuses.js | 2 +- tests/account/transaction.js | 6 +-- tests/account/transactions.js | 10 ++--- tests/account/transfer.js | 2 +- tests/account/unpair.js | 16 ++++---- tests/card/limits.js | 62 +++++++++++++++++++++++++++++ tests/fixtures/auth.js | 2 +- tests/fixtures/data.js | 22 ++++++++--- tests/index.js | 14 ++++--- tests/unmock.js | 72 ++++++++++++++++++++++++---------- 26 files changed, 307 insertions(+), 118 deletions(-) create mode 100644 lib/card.js create mode 100644 tests/card/limits.js diff --git a/lib/account.js b/lib/account.js index a91b488..a44bf7b 100644 --- a/lib/account.js +++ b/lib/account.js @@ -1,6 +1,8 @@ 'use strict'; const Promise = require('bluebird'); +const Card = require('./card.js'); + const api = require('./api'); const utils = require('./utils'); @@ -41,17 +43,9 @@ const utils = require('./utils'); /** * @typedef cards * - * @property {objet} paging - * @property {number} paging.totalResults Total amount of addresses - * @property {Object[]} data - * @property {String} data.id - * @property {String} data.cardType Card type - * @property {String} data.n26Status Card status - * @property {String} data.maskedPan Number card (masked) - * @property {String} data.expirationDate Expiration date - * @property {String} data.pinDefined - * @property {String} data.cardActivated - * @property {String} data.usernameOnCard Name on the card + * @property {objet} paging + * @property {number} paging.totalResults Total amount of addresses + * @property {Card[]} data */ /** @@ -328,12 +322,23 @@ class Account { } /** - * Get cards + * Get one specific or all cards * - * @return {Promise} + * @param {String} [cardId] Card Id + * + * @return {Promise|Promise} */ - cards() { - return utils.callApi(this, 'getCards'); + cards(cardId) { + if (cardId) { + return utils.callApi(this, 'getCard', cardId) + .then((card) => new Card(this, card)); + } + + return utils.callApi(this, 'getCards') + .then(cards => ({ + paging: cards.paging, + data: cards.data.map((card) => new Card(this, card)) + })); } /** diff --git a/lib/api.js b/lib/api.js index e9ffd75..a25512f 100644 --- a/lib/api.js +++ b/lib/api.js @@ -103,6 +103,17 @@ module.exports = { .catch(errorHandler); }, + getCard(token, cardId) { + return request.get({ + url: `${api}/api/cards/${cardId}`, + json: true, + headers: { + Authorization: `Bearer ${token}` + } + }) + .catch(errorHandler); + }, + getCards(token) { return request.get({ url: `${api}/api/cards`, @@ -114,6 +125,17 @@ module.exports = { .catch(errorHandler); }, + getCardLimits(token, cardId) { + return request.get({ + url: `${api}/api/settings/limits/${cardId}`, + json: true, + headers: { + Authorization: `Bearer ${token}` + } + }) + .catch(errorHandler); + }, + getContacts(token) { return request.get({ url: `${api}/api/smrt/contacts`, diff --git a/lib/card.js b/lib/card.js new file mode 100644 index 0000000..bdb35a1 --- /dev/null +++ b/lib/card.js @@ -0,0 +1,41 @@ +'use strict'; + +const utils = require('./utils'); + +/** + * @typedef cardLimit + * + * @property {String} limit Limit type + * @property {Number} [amount] Amount + * @property {String[]} [countryList] Array of country code + */ + +/** + * Card instance + * linked to an account. An account should contains many card. + */ +class Card { + constructor(account, card) { + this.account = account; + this.id = card.id; + this.cardType = card.cardType; + this.n26Status = card.n26Status; + this.maskedPan = card.maskedPan; + this.expirationDate = card.expirationDate; + this.pinDefined = card.pinDefined; + this.cardActivated = card.cardActivated; + this.usernameOnCard = card.usernameOnCard; + } + + /** + * Get card limit + * + * @return {Promise} + */ + limits() { + return utils.callApi(this.account, 'getCardLimits', this.id); + } +} + +module.exports = Card; + diff --git a/tests/account/account.js b/tests/account/account.js index 53a5fd2..7f9453e 100644 --- a/tests/account/account.js +++ b/tests/account/account.js @@ -26,7 +26,7 @@ describe('account', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/accounts') .reply(200, { status: 'OPEN_PRIMARY_ACCOUNT', diff --git a/tests/account/addresses.js b/tests/account/addresses.js index 7103b9e..5170a5a 100644 --- a/tests/account/addresses.js +++ b/tests/account/addresses.js @@ -26,7 +26,7 @@ describe('addresses', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/addresses') .reply(200, { paging: { diff --git a/tests/account/auth.js b/tests/account/auth.js index 3c58db4..0400841 100644 --- a/tests/account/auth.js +++ b/tests/account/auth.js @@ -32,13 +32,13 @@ describe('auth', () => { password: 'password', grant_type: 'password' }) - .reply(200, data); + .reply(200, data.account); api = nock('https://api.tech26.de') .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/me') .reply(200, { email: 'g.loutre@mail.com', diff --git a/tests/account/barzahlen.js b/tests/account/barzahlen.js index acefdff..48dd1bc 100644 --- a/tests/account/barzahlen.js +++ b/tests/account/barzahlen.js @@ -37,7 +37,7 @@ describe('barzahlen', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/barzahlen/check') .reply(200, dataBarzahlen); diff --git a/tests/account/cards.js b/tests/account/cards.js index 8ea3467..02a7c04 100644 --- a/tests/account/cards.js +++ b/tests/account/cards.js @@ -10,6 +10,8 @@ chai.use(dirtyChai); let n26; const data = require('../fixtures/data'); +const Card = require('../../lib/card.js'); + beforeEach((done) => { require('../fixtures/auth')((err, m) => { n26 = m; @@ -19,35 +21,14 @@ beforeEach((done) => { }); describe('cards', () => { - let api; - - beforeEach(() => { - api = nock('https://api.tech26.de') - .defaultReplyHeaders({ - 'Content-Type': 'application/json' - }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) - .get('/api/cards') - .reply(200, { - paging: { - totalResults: 1 - }, - data: [{ - maskedPan: '517337******4242', - expirationDate: 1548870576000, - cardType: 'MASTERCARD', - n26Status: 'ACTIVE', - pinDefined: 1454698655841, - cardActivated: 1454698679301, - usernameOnCard: 'GEORGE LOUTRE', - id: '203f3cc1-1bbb-4a3a-861c-2ac21fd8a77e' - }] - }); - }); - it('should return cards', () => { - return n26.cards().then((account) => { - expect(account).to.be.eql({ + const api = nock('https://api.tech26.de') + .defaultReplyHeaders({ + 'Content-Type': 'application/json' + }) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) + .get('/api/cards') + .reply(200, { paging: { totalResults: 1 }, @@ -62,10 +43,42 @@ describe('cards', () => { id: '203f3cc1-1bbb-4a3a-861c-2ac21fd8a77e' }] }); + + return n26.cards().then((cards) => { + expect(cards).to.have.deep.property('paging.totalResults', 1); + expect(cards).to.have.property('data'); + + cards.data.forEach((card) => { + expect(card).to.be.an.instanceof(Card); + + expect(api.isDone()).to.be.true(); + }); }); }); - afterEach((done) => { - done((!api.isDone()) ? new Error('Request not done') : null); + it('should return card', () => { + const cardId = '203f3cc1-1bbb-4a3a-861c-2ac21fd8a77e'; + const api = nock('https://api.tech26.de') + .defaultReplyHeaders({ + 'Content-Type': 'application/json' + }) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) + .get(`/api/cards/${cardId}`) + .reply(200, { + maskedPan: '517337******4242', + expirationDate: 1548870576000, + cardType: 'MASTERCARD', + n26Status: 'ACTIVE', + pinDefined: 1454698655841, + cardActivated: 1454698679301, + usernameOnCard: 'GEORGE LOUTRE', + id: '203f3cc1-1bbb-4a3a-861c-2ac21fd8a77e' + }); + + return n26.cards(cardId).then((card) => { + expect(card).to.be.an.instanceof(Card); + + expect(api.isDone()).to.be.true(); + }); }); }); diff --git a/tests/account/contacts.js b/tests/account/contacts.js index 2ad0d72..5113483 100644 --- a/tests/account/contacts.js +++ b/tests/account/contacts.js @@ -36,7 +36,7 @@ describe('contacts', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/contacts') .reply(200, dataContacts); }); diff --git a/tests/account/invitations.js b/tests/account/invitations.js index 0ffebc5..fe739ff 100644 --- a/tests/account/invitations.js +++ b/tests/account/invitations.js @@ -25,7 +25,7 @@ describe('invitations', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/aff/invitations') .reply(200, [{ invited: 'example@example.com', @@ -51,7 +51,7 @@ describe('invitations', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/aff/invite', {email: 'example@mail.com'}) .reply(200); @@ -65,14 +65,14 @@ describe('invitations', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/aff/invite', {email: 'example@mail.com'}) .reply(200); const apiInvite2 = nock('https://api.tech26.de') .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/aff/invite', {email: 'example2@mail.com'}) .reply(200); diff --git a/tests/account/limits.js b/tests/account/limits.js index e30edc4..35a1d92 100644 --- a/tests/account/limits.js +++ b/tests/account/limits.js @@ -24,7 +24,7 @@ describe('limits', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/settings/account/limits') .reply(200, [{ limit: 'ATM_DAILY_ACCOUNT', @@ -52,7 +52,7 @@ describe('limits', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/settings/account/limits', {limit: 'ATM_DAILY_ACCOUNT', amount: 200}) .reply(200); @@ -60,7 +60,7 @@ describe('limits', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/settings/account/limits', {limit: 'POS_DAILY_ACCOUNT', amount: 0}) .reply(200); diff --git a/tests/account/me.js b/tests/account/me.js index 2a681e0..dc96e7d 100644 --- a/tests/account/me.js +++ b/tests/account/me.js @@ -24,7 +24,7 @@ describe('me', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/me') .reply(200, { email: 'g.loutre@mail.com', @@ -74,7 +74,7 @@ describe('me', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/me') .query({full: true}) .reply(200, { diff --git a/tests/account/memo.js b/tests/account/memo.js index e3c85b4..f571172 100644 --- a/tests/account/memo.js +++ b/tests/account/memo.js @@ -29,7 +29,7 @@ describe('Create or update Memo', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post(`/api/transactions/${smartLinkId}/memo`, { memo: 'Hello' }) @@ -39,7 +39,7 @@ describe('Create or update Memo', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get(`/api/transactions/${smartLinkId}/metadata`) .reply(200); }); @@ -55,7 +55,7 @@ describe('Create or update Memo', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .put(`/api/transactions/${smartLinkId}/memo`, { memo: 'Tata' }) @@ -65,7 +65,7 @@ describe('Create or update Memo', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get(`/api/transactions/${smartLinkId}/metadata`) .reply(200, { memo: 'hello' diff --git a/tests/account/recipients.js b/tests/account/recipients.js index dfeffd3..cb243a2 100644 --- a/tests/account/recipients.js +++ b/tests/account/recipients.js @@ -26,7 +26,7 @@ describe('recipients', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/transactions/recipients') .reply(200, [{ iban: 'NL20ABNA0581855476', diff --git a/tests/account/statement.js b/tests/account/statement.js index 2d88851..cdc5495 100644 --- a/tests/account/statement.js +++ b/tests/account/statement.js @@ -25,7 +25,7 @@ describe('statement', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/statements/json/statement-2016-05') .reply(200, { id: 'statement-2016-05', @@ -46,7 +46,7 @@ describe('statement', () => { .defaultReplyHeaders({ 'Content-Type': 'application/pdf' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/statements/statement-2016-05') .reply(200, fs.readFileSync(`${__dirname}/../fixtures/statement.pdf`)); diff --git a/tests/account/statements.js b/tests/account/statements.js index 1344f5e..eaaff75 100644 --- a/tests/account/statements.js +++ b/tests/account/statements.js @@ -32,7 +32,7 @@ describe('statements', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/statements') .reply(200, dataStatements); diff --git a/tests/account/statuses.js b/tests/account/statuses.js index 2ffa45d..cf44481 100644 --- a/tests/account/statuses.js +++ b/tests/account/statuses.js @@ -47,7 +47,7 @@ describe('statuses', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/me/statuses') .reply(200, dataStatuses); diff --git a/tests/account/transaction.js b/tests/account/transaction.js index f3c7228..1776195 100644 --- a/tests/account/transaction.js +++ b/tests/account/transaction.js @@ -29,7 +29,7 @@ describe('transaction', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions/bbd24eb7-925a-48dd-9c1e-75bb9f514d78') .reply(200, { id: 'bbd24eb7-925a-48dd-9c1e-75bb9f514d78', @@ -93,7 +93,7 @@ describe('transaction', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions/bbd24eb7-925a-48dd-9c1e-75bb9f514d78') .reply(200, { id: 'bbd24eb7-925a-48dd-9c1e-75bb9f514d78', @@ -125,7 +125,7 @@ describe('transaction', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/transactions/1125318169-598002/metadata') .reply(200, { memo: 'tata yolo' diff --git a/tests/account/transactions.js b/tests/account/transactions.js index 13d27d1..979eccf 100644 --- a/tests/account/transactions.js +++ b/tests/account/transactions.js @@ -26,7 +26,7 @@ describe('transactions', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions') .query({limit: 50, pending: false}) .reply(200, [{ @@ -87,7 +87,7 @@ describe('transactions', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions') .query({limit: 20, pending: false}) .reply(200, [{ @@ -123,7 +123,7 @@ describe('transactions', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions') .query({limit: 50, categories: 'micro-education,micro-atm', pending: false}) .reply(200, []); @@ -136,7 +136,7 @@ describe('transactions', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions') .query({limit: 50, from: 1454630400000, to: 1454803199999, pending: false}) .reply(200, []); @@ -149,7 +149,7 @@ describe('transactions', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .get('/api/smrt/transactions') .query({limit: 50, textFilter: 'loutre', pending: false}) .reply(200, []); diff --git a/tests/account/transfer.js b/tests/account/transfer.js index a56beb8..a60f1bd 100644 --- a/tests/account/transfer.js +++ b/tests/account/transfer.js @@ -27,7 +27,7 @@ describe('transfer', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/transactions', { pin: '1234', transaction: { diff --git a/tests/account/unpair.js b/tests/account/unpair.js index 8e0e148..34e976f 100644 --- a/tests/account/unpair.js +++ b/tests/account/unpair.js @@ -32,7 +32,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/uppstart') .reply(200, { success: true, @@ -44,7 +44,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/verify/04e0a432-ee9a-4cf9-b384-3480f838e3e0') .reply(200, { success: true, @@ -56,7 +56,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/validation/pin', { pin: '1234' }) @@ -71,7 +71,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/validation/card/1234567890') .reply(200, { success: true, @@ -83,7 +83,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/validation/sms/send') .reply(200, { success: true, @@ -95,7 +95,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/validation/sms/verify/12345') .reply(200); }); @@ -134,7 +134,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/uppstart') .reply(200, { success: true, @@ -146,7 +146,7 @@ describe('Unpair mobile', () => { .defaultReplyHeaders({ 'Content-Type': 'application/json' }) - .matchHeader('Authorization', `Bearer ${data.access_token}`) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) .post('/api/unpair/verify/04e0a432-ee9a-4cf9-b384-3480f838e3e0') .reply(200, { success: false, diff --git a/tests/card/limits.js b/tests/card/limits.js new file mode 100644 index 0000000..d34f28b --- /dev/null +++ b/tests/card/limits.js @@ -0,0 +1,62 @@ +'use strict'; +/* eslint-disable global-require, max-len, arrow-body-style */ +const nock = require('nock'); +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +const Card = require('../../lib/card.js'); + +chai.use(dirtyChai); + +let card; +const data = require('../fixtures/data'); + +beforeEach((done) => { + require('../fixtures/auth')((err, m) => { + card = new Card(m, data.card); + + done(); + }); +}); + +describe('limits', () => { + it('should return limits', () => { + const limits = [{ + limit: 'POS_TRANSACTION', + amount: 10000 + }, { + limit: 'ATM_TRANSACTION_MONTLY', + amount: 20000 + }, { + limit: 'POS_TRANSACTION_MONTHLY', + amount: 20000 + }, { + limit: 'ATM_TRANSACTION', + amount: 600 + }, { + limit: 'E_COMMERCE_TRANSACTION', + amount: 5000 + }, { + limit: 'E_COMMERCE_TRANSACTION_MONTHLY', + amount: 20000 + }, { + limit: 'COUNTRY_LIST', + countryList: [] + }]; + + const apiLimits = nock('https://api.tech26.de') + .defaultReplyHeaders({ + 'Content-Type': 'application/json' + }) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) + .get(`/api/settings/limits/${card.id}`) + .reply(200, limits); + + return card.limits().then((l) => { + expect(l).to.be.eql(limits); + + expect(apiLimits.isDone()).to.be.true(); + }); + }); +}); diff --git a/tests/fixtures/auth.js b/tests/fixtures/auth.js index 23b57f4..73e041e 100644 --- a/tests/fixtures/auth.js +++ b/tests/fixtures/auth.js @@ -13,7 +13,7 @@ module.exports = (cb) => { password: 'password', grant_type: 'password' }) - .reply(200, data); + .reply(200, data.account); return new Number26('username@mail.com', 'password').asCallback(cb); }; diff --git a/tests/fixtures/data.js b/tests/fixtures/data.js index 7e04e8c..2711229 100644 --- a/tests/fixtures/data.js +++ b/tests/fixtures/data.js @@ -1,8 +1,20 @@ /* eslint-disable max-len */ module.exports = { - access_token: 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NTU1NDg5NDcsInVzZXJfbmFtZSI6Imlsb3Zlb3R0ZXJzQG1haWwuY29tIiwiYXV0aG9yaXRpZXMiOlsiVVNFUiJdLCJqdGkiOiJjZDE4NTE4OS01NGVlLTRlNzUtOTg1OC1lODU2ZDdmNTkwZWQiLCJjbGllbnRfaWQiOiJteS10cnVzdGVkLXdkcENsaWVudCIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSIsInRydXN0Il19.YeK8nhxyDLZkmPGA4at4yzhJcRzn2Y_iS5NJBenI4iY', - token_type: 'bearer', - expires_in: 1799, - scope: 'read write trust', - jti: 'cd185189-54ee-4e75-9858-e856d7f590ed' + account: { + access_token: 'eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0NTU1NDg5NDcsInVzZXJfbmFtZSI6Imlsb3Zlb3R0ZXJzQG1haWwuY29tIiwiYXV0aG9yaXRpZXMiOlsiVVNFUiJdLCJqdGkiOiJjZDE4NTE4OS01NGVlLTRlNzUtOTg1OC1lODU2ZDdmNTkwZWQiLCJjbGllbnRfaWQiOiJteS10cnVzdGVkLXdkcENsaWVudCIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSIsInRydXN0Il19.YeK8nhxyDLZkmPGA4at4yzhJcRzn2Y_iS5NJBenI4iY', + token_type: 'bearer', + expires_in: 1799, + scope: 'read write trust', + jti: 'cd185189-54ee-4e75-9858-e856d7f590ed' + }, + card: { + maskedPan: '517337******4242', + expirationDate: 1548870576000, + cardType: 'MASTERCARD', + n26Status: 'ACTIVE', + pinDefined: 1454698655841, + cardActivated: 1454698679301, + usernameOnCard: 'GEORGE LOUTRE', + id: '203f3cc1-1bbb-4a3a-861c-2ac21fd8a77e' + } }; diff --git a/tests/index.js b/tests/index.js index 58405ec..b2a0e4e 100644 --- a/tests/index.js +++ b/tests/index.js @@ -23,7 +23,7 @@ describe('Create instance', () => { password: 'password', grant_type: 'password' }) - .reply(200, data); + .reply(200, data.account); }); it('should pass identifiants to oauth endpoint', () => { @@ -40,9 +40,9 @@ describe('Create instance', () => { expect(m.email).to.be.eql('username@mail.com'); expect(m.password).to.be.eql('password'); expect(m.createdAt).to.exist().and.be.a('number'); - expect(m.accessToken).to.be.equal(data.access_token); - expect(m.expiresIn).to.be.equal(data.expires_in); - expect(m.jti).to.be.equal(data.jti); + expect(m.accessToken).to.be.equal(data.account.access_token); + expect(m.expiresIn).to.be.equal(data.account.expires_in); + expect(m.jti).to.be.equal(data.account.jti); expect(m.scope).to.be.equal('read write trust'); expect(m.tokenType).to.be.equal('bearer'); }) @@ -60,7 +60,7 @@ describe('Create instance', () => { password: 'password', grant_type: 'password' }) - .reply(200, data); + .reply(200, data.account); return Promise.all([ new Number26('username@mail.com', 'password'), @@ -137,3 +137,7 @@ describe('account', () => { require('./account/transfer'); require('./account/unpair'); }); + +describe('card', () => { + require('./card/limits'); +}); diff --git a/tests/unmock.js b/tests/unmock.js index 3671857..60fad54 100644 --- a/tests/unmock.js +++ b/tests/unmock.js @@ -259,28 +259,58 @@ describe('Create instance', function () { // eslint-disable-line func-names }); }); - it('should get cards', () => { - return n26.cards() - .then((cards) => { - expect(cards).to.be.an('object'); - expect(cards).to.have.deep.property('paging.totalResults'); - expect(cards).to.have.property('data').that.is.an('array'); - - console.log(`\t${cards.paging.totalResults} cards`); - - cards.data.forEach(c => { - expect(c).to.have.property('maskedPan'); - expect(c).to.have.property('expirationDate'); - expect(c).to.have.property('cardType'); - expect(c).to.have.property('n26Status'); - expect(c).to.have.property('pinDefined'); - expect(c).to.have.property('cardActivated'); - expect(c).to.have.property('usernameOnCard'); - expect(c).to.have.property('id'); - - console.log(`\t- ${c.cardType} ${c.n26Status} ${c.maskedPan}`); + describe('Card', () => { + let card; + + before((done) => { + return n26.cards() + .then((cards) => { + card = cards.data[0]; + + done(); }); - }); + }); + + it('should get cards', () => { + return n26.cards() + .then((cards) => { + expect(cards).to.be.an('object'); + expect(cards).to.have.deep.property('paging.totalResults'); + expect(cards).to.have.property('data').that.is.an('array'); + + console.log(`\t${cards.paging.totalResults} cards`); + + cards.data.forEach(c => { + expect(c).to.have.property('maskedPan'); + expect(c).to.have.property('expirationDate'); + expect(c).to.have.property('cardType'); + expect(c).to.have.property('n26Status'); + expect(c).to.have.property('pinDefined'); + expect(c).to.have.property('cardActivated'); + expect(c).to.have.property('usernameOnCard'); + expect(c).to.have.property('id'); + + console.log(`\t- ${c.cardType} ${c.n26Status} ${c.maskedPan}`); + }); + }); + }); + + it('should get card limits', () => { + return card.limits() + .then((limits) => { + expect(limits).to.be.an('Array'); + limits.forEach((l) => { + expect(l).to.have.property('limit'); + if (l.limit === 'COUNTRY_LIST') { + expect(l).to.have.property('countryList'); + } else { + expect(l).to.have.property('amount'); + } + }); + + console.log(`\tCard limits ${limits[0].limit} -> ${limits[0].amount}`); + }); + }); }); it('should get addresses', () => { From fca804b57ae09a29442c2e6626e347feee9331e9 Mon Sep 17 00:00:00 2001 From: Pierrick Date: Wed, 15 Jun 2016 09:46:30 +0200 Subject: [PATCH 2/6] Block / unblock card Issue #11 --- lib/api.js | 22 +++++++++++++++++++ lib/card.js | 20 +++++++++++++++++ tests/card/block.js | 53 +++++++++++++++++++++++++++++++++++++++++++++ tests/index.js | 1 + tests/unmock.js | 20 +++++++++++++++++ 5 files changed, 116 insertions(+) create mode 100644 tests/card/block.js diff --git a/lib/api.js b/lib/api.js index a25512f..aa860fa 100644 --- a/lib/api.js +++ b/lib/api.js @@ -27,6 +27,28 @@ module.exports = { .catch(errorHandler); }, + blockCard(token, cardId) { + return request.post({ + url: `${api}/api/cards/${cardId}/block`, + json: true, + headers: { + Authorization: `Bearer ${token}` + } + }) + .catch(errorHandler); + }, + + unblockCard(token, cardId) { + return request.post({ + url: `${api}/api/cards/${cardId}/unblock`, + json: true, + headers: { + Authorization: `Bearer ${token}` + } + }) + .catch(errorHandler); + }, + checkBarzahlen(token) { return request.get({ url: `${api}/api/barzahlen/check`, diff --git a/lib/card.js b/lib/card.js index bdb35a1..7d6d6d0 100644 --- a/lib/card.js +++ b/lib/card.js @@ -35,6 +35,26 @@ class Card { limits() { return utils.callApi(this.account, 'getCardLimits', this.id); } + + /** + * Block card + * + * @return {Promise<>} + */ + block() { + return utils.callApi(this.account, 'blockCard', this.id) + .tap(() => { this.n26Status = 'BLOCKED'; }); + } + + /** + * Unblock card + * + * @return {Promise<>} + */ + unblock() { + return utils.callApi(this.account, 'unblockCard', this.id) + .tap(() => { this.n26Status = 'ACTIVE'; }); + } } module.exports = Card; diff --git a/tests/card/block.js b/tests/card/block.js new file mode 100644 index 0000000..230e68c --- /dev/null +++ b/tests/card/block.js @@ -0,0 +1,53 @@ +'use strict'; +/* eslint-disable global-require, max-len, arrow-body-style */ +const nock = require('nock'); +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +const Card = require('../../lib/card.js'); + +chai.use(dirtyChai); + +let card; +const data = require('../fixtures/data'); + +beforeEach((done) => { + require('../fixtures/auth')((err, m) => { + card = new Card(m, data.card); + + done(); + }); +}); + +describe('block / unblock', () => { + it('should block', () => { + const apiLimits = nock('https://api.tech26.de') + .defaultReplyHeaders({ + 'Content-Type': 'application/json' + }) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) + .post(`/api/cards/${card.id}/block`) + .reply(200); + + return card.block().then(() => { + expect(card.n26Status).to.be.eql('BLOCKED'); + expect(apiLimits.isDone()).to.be.true(); + }); + }); + + it('should unblock', () => { + const apiLimits = nock('https://api.tech26.de') + .defaultReplyHeaders({ + 'Content-Type': 'application/json' + }) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) + .post(`/api/cards/${card.id}/unblock`) + .reply(200); + + return card.unblock().then(() => { + expect(card.n26Status).to.be.eql('ACTIVE'); + expect(apiLimits.isDone()).to.be.true(); + }); + }); +}); diff --git a/tests/index.js b/tests/index.js index b2a0e4e..0e79295 100644 --- a/tests/index.js +++ b/tests/index.js @@ -139,5 +139,6 @@ describe('account', () => { }); describe('card', () => { + require('./card/block'); require('./card/limits'); }); diff --git a/tests/unmock.js b/tests/unmock.js index 60fad54..566e4d8 100644 --- a/tests/unmock.js +++ b/tests/unmock.js @@ -311,6 +311,26 @@ describe('Create instance', function () { // eslint-disable-line func-names console.log(`\tCard limits ${limits[0].limit} -> ${limits[0].amount}`); }); }); + + it('should block card', () => { + return card.block() + .then(() => { + return n26.cards(card.id); + }) + .then((c) => { + console.log(`\tCard n26Status: ${c.n26Status}`); + }); + }); + + it('should unblock card', () => { + return card.unblock() + .then(() => { + return n26.cards(card.id); + }) + .then((c) => { + console.log(`\tCard n26Status: ${c.n26Status}`); + }); + }); }); it('should get addresses', () => { From 3f11dd786522944238eab8dfead027d1c2fea030 Mon Sep 17 00:00:00 2001 From: Pierrick Date: Wed, 15 Jun 2016 11:04:32 +0200 Subject: [PATCH 3/6] Set limits for card Issue #12 --- lib/api.js | 11 +++++++++++ lib/card.js | 26 ++++++++++++++++++++++---- tests/card/limits.js | 32 ++++++++++++++++++++++++++++++++ tests/unmock.js | 17 +++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/lib/api.js b/lib/api.js index aa860fa..b3f6abb 100644 --- a/lib/api.js +++ b/lib/api.js @@ -158,6 +158,17 @@ module.exports = { .catch(errorHandler); }, + setCardLimits(token, opt) { + return request.put({ + url: `${api}/api/settings/limits/${opt.cardId}`, + json: opt.limit, + headers: { + Authorization: `Bearer ${token}` + } + }) + .catch(errorHandler); + }, + getContacts(token) { return request.get({ url: `${api}/api/smrt/contacts`, diff --git a/lib/card.js b/lib/card.js index 7d6d6d0..c87ea47 100644 --- a/lib/card.js +++ b/lib/card.js @@ -1,5 +1,7 @@ 'use strict'; +const Promise = require('bluebird'); + const utils = require('./utils'); /** @@ -28,12 +30,29 @@ class Card { } /** - * Get card limit + * Get / set card limit + * + * @property {Object} [limit] + * @property {String} limit.limit Limit type + * @property {Number} [limit.amount] Amount + * @property {String[]} [limit.countryList] Array of country code * * @return {Promise} */ - limits() { - return utils.callApi(this.account, 'getCardLimits', this.id); + limits(limit) { + return Promise.try(() => { + if (limit) { + if (!limit.limit || + (limit.limit === 'COUNTRY_LIST' && !limit.countryList) || + (limit.limit !== 'COUNTRY_LIST' && limit.amount === undefined)) { + throw new Error('BAD_PARAMS'); + } + + return utils.callApi(this.account, 'setCardLimits', {cardId: this.id, limit}); + } + + return utils.callApi(this.account, 'getCardLimits', this.id); + }); } /** @@ -58,4 +77,3 @@ class Card { } module.exports = Card; - diff --git a/tests/card/limits.js b/tests/card/limits.js index d34f28b..cb6620f 100644 --- a/tests/card/limits.js +++ b/tests/card/limits.js @@ -59,4 +59,36 @@ describe('limits', () => { expect(apiLimits.isDone()).to.be.true(); }); }); + + describe('Set limit', () => { + it('should set limit', () => { + const limit = { + limit: 'E_COMMERCE_TRANSACTION', + amount: 5000 + }; + + const apiLimit = nock('https://api.tech26.de') + .defaultReplyHeaders({ + 'Content-Type': 'application/json' + }) + .matchHeader('Authorization', `Bearer ${data.account.access_token}`) + .put(`/api/settings/limits/${card.id}`, limit) + .reply(200, limit); + + return card.limits(limit).then((l) => { + expect(l).to.be.eql(limit); + + expect(apiLimit.isDone()).to.be.true(); + }); + }); + + it('should return error if bad limit', () => { + return card.limits({ + limit: 'E_COMMERCE_TRANSACTION' + }).catch((err) => { + expect(err).to.be.an.instanceOf(Error); + expect(err.message).to.equal('BAD_PARAMS'); + }); + }); + }); }); diff --git a/tests/unmock.js b/tests/unmock.js index 566e4d8..87f0eec 100644 --- a/tests/unmock.js +++ b/tests/unmock.js @@ -261,6 +261,7 @@ describe('Create instance', function () { // eslint-disable-line func-names describe('Card', () => { let card; + let limitOnline; before((done) => { return n26.cards() @@ -305,6 +306,10 @@ describe('Create instance', function () { // eslint-disable-line func-names expect(l).to.have.property('countryList'); } else { expect(l).to.have.property('amount'); + + if (l.limit === 'E_COMMERCE_TRANSACTION') { + limitOnline = l.amount; + } } }); @@ -312,6 +317,18 @@ describe('Create instance', function () { // eslint-disable-line func-names }); }); + it('should set card limits', () => { + return card.limits({ + amount: (limitOnline) ? 0 : 5000, + limit: 'E_COMMERCE_TRANSACTION' + }) + .then((l) => { + console.log(`\tCard set limit ${l.limit}: ${l.amount}`); + + return card.limits({amount: limitOnline, limit: 'E_COMMERCE_TRANSACTION'}); + }); + }); + it('should block card', () => { return card.block() .then(() => { From f21229a6e6ca3acd4a3c57c81da286eac5ebff67 Mon Sep 17 00:00:00 2001 From: Pierrick Date: Thu, 16 Jun 2016 16:44:50 +0200 Subject: [PATCH 4/6] =?UTF-8?q?Add=20questions=20for=20unmocked=20tests=20?= =?UTF-8?q?=F0=9F=91=8F=20+=20More=20easier=20to=20active=20optionnal=20te?= =?UTF-8?q?sts=20+=20Save=20data=20on=20~/.config/configstore/number26-unm?= =?UTF-8?q?ock.json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +- package.json | 4 +- tests/unmock.js | 716 ++++++----------------------------- tests/unmock/barzahlen.js | 69 ++++ tests/unmock/cards.js | 99 +++++ tests/unmock/index.js | 258 +++++++++++++ tests/unmock/invitations.js | 33 ++ tests/unmock/statements.js | 56 +++ tests/unmock/transactions.js | 99 +++++ tests/unmock/transfer.js | 42 ++ tests/unmock/unpair.js | 34 ++ 11 files changed, 807 insertions(+), 610 deletions(-) create mode 100644 tests/unmock/barzahlen.js create mode 100644 tests/unmock/cards.js create mode 100644 tests/unmock/index.js create mode 100644 tests/unmock/invitations.js create mode 100644 tests/unmock/statements.js create mode 100644 tests/unmock/transactions.js create mode 100644 tests/unmock/transfer.js create mode 100644 tests/unmock/unpair.js diff --git a/README.md b/README.md index b688743..8a7174e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Not all endpoints are available yet. + [x] Get Transactions (with search) + [x] Add / update memo on transactions + [x] Create a transfert -+ [ ] unpair ++ [x] unpair + [ ] pair + [ ] certify transfer @@ -103,7 +103,4 @@ Run `npm test` for full mocked testing with coverage. Less asserts. Used for detect api change. -Run `TEST_EMAIL=exemple@mail.com TEST_PASSWORD=ilovemylittlepony TRANSFER_IBAN=FR7630001007941234567890185 TRANSFER_BIC=BNPAFRPP TRANSFER_NAME="George Loutre" TRANSFER_PIN=123456 npm run test-unmock` for un-mocked test. - -The transfer test (*0.01 €) can be switch off with env `NO_TRANSFER`. -Otherwise, *it should be confirmed* in the app. +Run `npm run test-unmock` for un-mocked test. diff --git a/package.json b/package.json index 5ea884e..2e52b2c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "test": "istanbul cover _mocha tests/index.js && if [ \"$CONTINUOUS_INTEGRATION\" = \"true\" ]; then cat ./coverage/lcov.info | ./node_modules/.bin/coveralls; fi", - "test-unmock": "_mocha -b tests/unmock.js", + "test-unmock": "node tests/unmock.js", "lint": "eslint *.js lib/* tests/*; exit 0", "docs": "jsdoc -c .jsdoc.json -R README.md", "deployDocs": "npm run docs && sh ./scripts/deployDocs.sh" @@ -30,12 +30,14 @@ }, "devDependencies": { "chai": "^3.2.0", + "configstore": "^2.0.0", "coveralls": "^2.11.9", "dirty-chai": "^1.2.2", "docdash": "^0.4.0", "eslint": "^2.8.0", "eslint-config-airbnb-base": "^3.0.1", "eslint-plugin-import": "^1.5.0", + "inquirer": "^1.0.3", "istanbul": "^0.4.2", "jsdoc": "^3.4.0", "mocha": "^2.2.5", diff --git a/tests/unmock.js b/tests/unmock.js index 87f0eec..819119f 100644 --- a/tests/unmock.js +++ b/tests/unmock.js @@ -1,609 +1,117 @@ 'use strict'; /* eslint-disable global-require, max-len, no-console, arrow-body-style */ -const readline = require('readline'); -const chai = require('chai'); -const dirtyChai = require('dirty-chai'); -const expect = chai.expect; -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - -const Number26 = require('../index'); - -chai.use(dirtyChai); - -const transactionsLimit = process.env.TRANSACTIONS_LIMIT || 2; -const commonTransactionFields = ['id', 'userId', 'type', 'amount', 'smartLinkId', 'linkId', 'accountId', 'category', 'cardId', 'pending', 'transactionNature', 'visibleTS', 'recurring']; -const transactionFields = { - PT: commonTransactionFields.concat(['currencyCode', 'originalAmount', 'originalCurrency', 'exchangeRate', 'merchantCity', 'mcc', 'mccGroup', 'merchantName', 'merchantId']), - DT: commonTransactionFields.concat(['partnerAccountIsSepa', 'partnerName', 'partnerIban', 'referenceText', 'userCertified', 'smartContactId']), - CT: commonTransactionFields.concat(['currencyCode', 'partnerBic', 'partnerAccountIsSepa', 'partnerName', 'partnerIban', 'referenceText', 'smartContactId', 'confirmed']), - AE: commonTransactionFields.concat(['currencyCode', 'originalAmount', 'originalCurrency', 'exchangeRate', 'merchantCity', 'mcc', 'mccGroup', 'merchantName', 'merchantId']), - AA: commonTransactionFields.concat(['currencyCode', 'originalAmount', 'originalCurrency', 'exchangeRate', 'merchantCity', 'mcc', 'mccGroup', 'merchantName', 'merchantId', 'transactionTerminal']) -}; -const meProperties = [ - 'userInfo.id', - 'userInfo.email', - 'userInfo.firstName', - 'userInfo.lastName', - 'userInfo.kycFirstName', - 'userInfo.kycLastName', - 'userInfo.title', - 'userInfo.gender', - 'userInfo.birthDate', - 'userInfo.signupCompleted', - 'userInfo.nationality', - 'userInfo.birthPlace', - 'userInfo.mobilePhoneNumber', - 'userInfo.shadowID', - 'account.status', - 'account.availableBalance', - 'account.usableBalance', - 'account.bankBalance', - 'account.iban', - 'account.id', - 'cards[0].maskedPan', - 'cards[0].expirationDate', - 'cards[0].cardType', - 'cards[0].exceetExpressCardDelivery', - 'cards[0].n26Status', - 'cards[0].pinDefined', - 'cards[0].cardActivated', - 'cards[0].id', - 'addresses[0].addressLine1', - 'addresses[0].streetName', - 'addresses[0].houseNumberBlock', - 'addresses[0].zipCode', - 'addresses[0].cityName', - 'addresses[0].countryName', - 'addresses[0].type', - 'addresses[0].id', - 'userFeatures', - 'userStatus.singleStepSignup', - 'userStatus.emailValidationInitiated', - 'userStatus.emailValidationCompleted', - 'userStatus.phonePairingInitiated', - 'userStatus.phonePairingCompleted', - 'userStatus.kycInitiated', - 'userStatus.kycCompleted', - 'userStatus.kycWebIDInitiated', - 'userStatus.kycWebIDCompleted', - 'userStatus.cardActivationCompleted', - 'userStatus.cardIssued', - 'userStatus.pinDefinitionCompleted', - 'userStatus.bankAccountCreationInitiated', - 'userStatus.bankAccountCreationSucceded', - 'userStatus.firstIncomingTransaction', - 'userStatus.smsVerificationCode', - 'userStatus.unpairTokenCreation', - 'userStatus.finoIntegrationStatus', - 'userStatus.id', - 'preference.locale', - 'preference.AAPushNotificationActive', - 'preference.AFPushNotificationActive', - 'preference.AVPushNotificationActive', - 'preference.ARPushNotificationActive', - 'preference.DTPushNotificationActive', - 'preference.CTPushNotificationActive', - 'preference.DDPushNotificationActive', - 'preference.DRPushNotificationActive', - 'preference.AAEmailNotificationActive', - 'preference.AFEmailNotificationActive', - 'preference.AVEmailNotificationActive', - 'preference.AREmailNotificationActive', - 'preference.DTEmailNotificationActive', - 'preference.CTEmailNotificationActive', - 'preference.DDEmailNotificationActive', - 'preference.DREmailNotificationActive', - 'preference.id', - 'userCustomSetting.RATING_DIALOG_SEEN', - 'userCustomSetting.TRANSFERWISE_DIALOG_SEEN', - 'userCustomSetting.OVERDRAFT_NOTIFY_UPGRADE', - 'userCustomSetting.user' -]; -const transactionProperties = [ - 'id', - 'userId', - 'type', - 'amount', - 'smartLinkId', - 'currencyCode', - 'originalAmount', - 'originalCurrency', - 'exchangeRate', - 'merchantCity', - 'visibleTS', - 'mcc', - 'mccGroup', - 'merchantName', - 'merchantId', - 'recurring', - 'linkId', - 'accountId', - 'category', - 'cardId', - 'pending', - 'transactionNature', - 'tags' -]; -const statusesProperties = [ - 'singleStepSignup', - 'emailValidationInitiated', - 'emailValidationCompleted', - 'phonePairingInitiated', - 'phonePairingCompleted', - 'kycInitiated', - 'kycCompleted', - 'kycWebIDInitiated', - 'kycWebIDCompleted', - 'cardActivationCompleted', - 'cardIssued', - 'pinDefinitionCompleted', - 'bankAccountCreationInitiated', - 'bankAccountCreationSucceded', - 'firstIncomingTransaction', - 'smsVerificationCode', - 'unpairTokenCreation', - 'finoIntegrationStatus', - 'id' -]; -const barzahlenProperties = [ - 'depositAllowance', - 'withdrawAllowance', - 'remainingAmountMonth', - 'feeRate', - 'cash26WithdrawalsCount', - 'cash26WithdrawalsSum', - 'atmWithdrawalsCount', - 'atmWithdrawalsSum', - 'monthlyDepositFeeThreshold', - 'success' -]; -const barzahlenBranchesProperties = [ - 'id', - 'lat', - 'lng', - 'title', - 'street_no', - 'zipcode', - 'city', - 'countrycode', - 'opening_hours', - 'logo_url', - 'logo_thumbnail_url', - 'minutes_until_close', - 'offline_partner_id' -]; -const statementsProperties = [ - 'id', - 'month', - 'url', - 'visibleTS', - 'year' -]; -const contactsProperties = [ - 'id', - 'name', - 'subtitle', - 'account.accountType', - 'account.iban', - 'account.bic' -]; - -describe('Create instance', function () { // eslint-disable-line func-names - this.timeout(5000); - let n26; - - it('should create instance', () => { - return new Number26(process.env.TEST_EMAIL, process.env.TEST_PASSWORD) - .then((m) => { - expect(m).to.be.exist(); - n26 = m; - }); +const Mocha = require('mocha'); +const inquirer = require('inquirer'); +const Configstore = require('configstore'); + +const mocha = new Mocha({bail: true}); +const config = new Configstore('number26-unmock', {options: {}}); + +function hasOptions(opts) { + return (answers) => { + opts = Array.isArray(opts) ? opts : [opts]; + + return !!opts.find((o) => answers.options.indexOf(o) !== -1); + }; +} + +const questions = [{ + type: 'input', + name: 'email', + message: 'Account email:', + default: process.env.NUMBER26_EMAIL || config.get('email') +}, { + type: 'password', + name: 'password', + message: 'Password account:', + default: process.env.NUMBER26_PASSWORD +}, { + type: 'checkbox', + message: 'Select options', + name: 'options', + choices: [{ + name: 'Statement', + checked: config.get('options.statement') ? config.get('options.statement') : true + }, { + name: 'Invitation', + default: process.env.NUMBER26_OPTIONS_INVITE || config.get('options.invite') + }, { + name: 'Transfer', + default: process.env.NUMBER26_OPTIONS_TRANSFER || config.get('options.transfer') + }, { + name: 'Unpair', + default: process.env.NUMBER26_OPTIONS_UNPAIR || config.get('options.unpair') + }] +}, { + type: 'password', + name: 'pin', + message: 'Pin number:', + default: process.env.NUMBER26_PIN, + validate: (pin) => pin.length === 4, + when: hasOptions(['Transfer', 'Unpair']) +}, { + type: 'input', + name: 'inviteEmail', + message: 'Send invitation to email:', + when: hasOptions('Invitation') +}, { + type: 'input', + name: 'transferIBAN', + message: 'IBAN transfer:', + default: process.env.NUMBER26_OPTIONS_TRANSFER_IBAN || config.get('transfer.iban'), + validate: (iban) => /[a-zA-Z]{2}[0-9]{2}[a-zA-Z0-9]{4}[0-9]{7}([a-zA-Z0-9]?){0,16}/.test(iban), + when: hasOptions('Transfer') +}, { + type: 'input', + name: 'transferBIC', + message: 'BIC transfer:', + default: process.env.NUMBER26_OPTIONS_TRANSFER_BIC || config.get('transfer.bic'), + validate: (bic) => /([a-zA-Z]{4}[a-zA-Z]{2}[a-zA-Z0-9]{2}([a-zA-Z0-9]{3})?)/.test(bic), + when: hasOptions('Transfer') +}, { + type: 'input', + name: 'transferNAME', + message: 'NAME transfer:', + default: process.env.NUMBER26_OPTIONS_TRANSFER_NAME || config.get('transfer.name'), + when: hasOptions('Transfer') +}, { + type: 'input', + name: 'cardNumber', + message: '10 digits on card:', + default: process.env.NUMBER26_OPTIONS_UNPAIR_CARDNUMBER || config.get('unpair.cardNumber'), + validate: (cardNumber) => cardNumber.length === 10, + when: hasOptions('Unpair') +}]; + +inquirer.prompt(questions).then((answers) => { + global.CONFIG = answers; + + config.set('email', answers.email); + config.set('options', {}); + + answers.options.forEach((opt) => { + if (opt === 'Invitation') { + config.set('options.invite', true); + } + + if (opt === 'Transfer') { + config.set('options.transfer', true); + config.set('transfer', { + iban: answers.transferIBAN, + bic: answers.transferBIC, + name: answers.transferNAME + }); + } + + if (opt === 'Unpair') { + config.set('options.unpair', true); + config.set('unpair.cardNumber', answers.cardNumber); + } + }); + + mocha.addFile(`${__dirname}/unmock/index.js`); + mocha.run((failures) => { + process.exit(failures); }); - - it('should get profil', () => { - return n26.me(true) - .then((profil) => { - meProperties.forEach(property => { - expect(profil).to.have.deep.property(property); - }); - - console.log(`\tMe: ${profil.userInfo.firstName} ${profil.userInfo.lastName}`); - }); - }); - - it('should get account', () => { - return n26.account() - .then((account) => { - expect(account).to.have.property('availableBalance'); - expect(account).to.have.property('bankBalance'); - expect(account).to.have.property('iban'); - expect(account).to.have.property('id'); - expect(account).to.have.property('status'); - expect(account).to.have.property('usableBalance'); - - - console.log(`\tAccount: ${account.status} ${account.iban}`); - }); - }); - - it('should check barzahlen', () => { - return n26.barzahlen() - .then((barzahlen) => { - barzahlenProperties.forEach(property => { - expect(barzahlen).to.have.deep.property(property); - }); - - console.log(`\tBarzahlen: ${barzahlen.depositAllowance} € deposit allowed`); - }); - }); - - it('should get barzahlen', () => { - return Number26.barzahlen({ - nelat: 52.6078, - nelon: 13.5338, - swlat: 52.4165, - swlon: 13.2688 - }) - .then((barzahlenBranches) => { - barzahlenBranches.forEach((branch) => { - barzahlenBranchesProperties.forEach(property => { - expect(branch).to.have.deep.property(property); - }); - }); - - console.log(`\tBarzahlen: ${barzahlenBranches.length} places in this zone`); - }); - }); - - describe('Card', () => { - let card; - let limitOnline; - - before((done) => { - return n26.cards() - .then((cards) => { - card = cards.data[0]; - - done(); - }); - }); - - it('should get cards', () => { - return n26.cards() - .then((cards) => { - expect(cards).to.be.an('object'); - expect(cards).to.have.deep.property('paging.totalResults'); - expect(cards).to.have.property('data').that.is.an('array'); - - console.log(`\t${cards.paging.totalResults} cards`); - - cards.data.forEach(c => { - expect(c).to.have.property('maskedPan'); - expect(c).to.have.property('expirationDate'); - expect(c).to.have.property('cardType'); - expect(c).to.have.property('n26Status'); - expect(c).to.have.property('pinDefined'); - expect(c).to.have.property('cardActivated'); - expect(c).to.have.property('usernameOnCard'); - expect(c).to.have.property('id'); - - console.log(`\t- ${c.cardType} ${c.n26Status} ${c.maskedPan}`); - }); - }); - }); - - it('should get card limits', () => { - return card.limits() - .then((limits) => { - expect(limits).to.be.an('Array'); - limits.forEach((l) => { - expect(l).to.have.property('limit'); - if (l.limit === 'COUNTRY_LIST') { - expect(l).to.have.property('countryList'); - } else { - expect(l).to.have.property('amount'); - - if (l.limit === 'E_COMMERCE_TRANSACTION') { - limitOnline = l.amount; - } - } - }); - - console.log(`\tCard limits ${limits[0].limit} -> ${limits[0].amount}`); - }); - }); - - it('should set card limits', () => { - return card.limits({ - amount: (limitOnline) ? 0 : 5000, - limit: 'E_COMMERCE_TRANSACTION' - }) - .then((l) => { - console.log(`\tCard set limit ${l.limit}: ${l.amount}`); - - return card.limits({amount: limitOnline, limit: 'E_COMMERCE_TRANSACTION'}); - }); - }); - - it('should block card', () => { - return card.block() - .then(() => { - return n26.cards(card.id); - }) - .then((c) => { - console.log(`\tCard n26Status: ${c.n26Status}`); - }); - }); - - it('should unblock card', () => { - return card.unblock() - .then(() => { - return n26.cards(card.id); - }) - .then((c) => { - console.log(`\tCard n26Status: ${c.n26Status}`); - }); - }); - }); - - it('should get addresses', () => { - return n26.addresses() - .then((addresses) => { - expect(addresses).to.be.an('object'); - expect(addresses).to.have.deep.property('paging.totalResults'); - expect(addresses).to.have.property('data').that.is.an('array'); - - console.log(`\t${addresses.paging.totalResults} addresses`); - - addresses.data.forEach(a => { - expect(a).to.have.property('addressLine1'); - expect(a).to.have.property('streetName'); - expect(a).to.have.property('houseNumberBlock'); - expect(a).to.have.property('zipCode'); - expect(a).to.have.property('cityName'); - expect(a).to.have.property('countryName'); - expect(a).to.have.property('type'); - expect(a).to.have.property('id'); - - console.log(`\t- ${a.type} ${a.addressLine1} ${a.streetName}`); - }); - }); - }); - - it('should get recipients', () => { - return n26.recipients() - .then((recipients) => { - expect(recipients).to.be.an('array'); - - console.log(`\t${recipients.length} recipients`); - - recipients.forEach(r => { - expect(r).to.have.property('iban'); - expect(r).to.have.property('name'); - expect(r).to.have.property('bic'); - - console.log(`\t- ${r.name} ${r.iban} ${r.bic}`); - }); - }); - }); - - it(`should get transactions - limit ${transactionsLimit}`, () => { - return n26.transactions({limit: transactionsLimit}) - .then((transactions) => { - expect(transactions).to.be.an('array'); - - console.log(`\tLast ${transactions.length} transactions`); - - transactions.forEach(t => { - expect(t).to.have.property('type'); - - if (!transactionFields[t.type]) { - console.log(t); - } - - transactionFields[t.type].forEach(f => { - expect(t).to.have.property(f); - }); - - console.log(`\t- ${t.type} ${t.amount} ${t.merchantName || t.partnerName}`); - }); - }); - }); - - it('should get transaction detail', () => { - return n26.transactions() - .then((transactions) => { - return n26.transaction(transactions[0].id).then(detail => { - transactionProperties.forEach(property => { - expect(detail).to.have.deep.property(property); - }); - }); - }); - }); - - it('should update memo on transaction', () => { - return n26.transactions() - .then((transactions) => { - return n26.transaction(transactions[0].id, {meta: true}).then(d => { - const previousMemo = d.meta.memo; - const newMemo = `YOLO${Math.round(Math.random() * 1000)}`; - - return n26.memo(transactions[0].smartLinkId, newMemo) - .then(() => { - return n26.transaction(transactions[0].id, {meta: true}).then(dd => { - expect(dd.meta.memo).to.be.eql(newMemo); - - return n26.memo(transactions[0].smartLinkId, previousMemo); - }); - }); - }); - }); - }); - - it('should return email invited', () => { - return n26.invitations().then((emails) => { - expect(emails).to.be.an('array'); - emails.forEach((e) => { - ['invited', 'status', 'reward', 'created'].forEach((p) => { - expect(e).to.have.property(p); - }); - }); - - console.log(`\t${emails[0].invited} was invited`); - }); - }); - - it('should return statuses', () => { - return n26.statuses().then((statuses) => { - statusesProperties.forEach(property => { - expect(statuses).to.have.deep.property(property); - }); - - console.log(`\tYour card was actived: ${new Date(statuses.cardActivationCompleted)}`); - }); - }); - - it('should get account limits', () => { - return n26.limits().then((limits) => { - console.log(`\tYour account is limited to ${limits[0].amount} for ${limits[0].limit}`); - }); - }); - - it('should set account limits', () => { - let previousAtmDailyAccount; - - return n26.limits().then((limits) => { - limits.forEach((limit) => { - if (limit.limit === 'ATM_DAILY_ACCOUNT') { - previousAtmDailyAccount = limit.amount; - } - }); - }) - .then(() => n26.limits({atm: 500})) - .then(() => n26.limits()) - .then((limits) => { - limits.forEach((limit) => { - if (limit.limit === 'ATM_DAILY_ACCOUNT') { - expect(limit.amount).to.be.eql(500); - } - }); - - return n26.limits({atm: previousAtmDailyAccount}); - }); - }); - - it('should get contacts', () => { - return n26.contacts().then((contacts) => { - contacts.forEach((contact) => { - contactsProperties.forEach(property => { - expect(contact).to.have.deep.property(property); - }); - }); - - console.log(`\tFirst contacts: ${contacts[0].name} ${contacts[0].subtitle}`); - }); - }); - - it('should return statements', () => { - return n26.statements().then((statements) => { - statements.forEach((statement) => { - statementsProperties.forEach((property) => { - expect(statement).to.have.deep.property(property); - }); - }); - - console.log(`\tLast statements for ${statements[0].month}/${statements[0].year}`); - }); - }); - - it('should get last statement file', function () { // eslint-disable-line func-names - this.timeout(25000); - - return n26.statements().then((statements) => statements[0].id) - .then(statementId => { - return Promise.all([ - n26.statement(statementId), - n26.statement(statementId, true) - ]); - }) - .spread((base64, pdf) => { - [base64, pdf].forEach((statement) => { - ['id', 'type', 'pdf'].forEach((property) => { - expect(statement).to.have.deep.property(property); - }); - }); - - expect(base64.pdf).to.be.a('String'); - expect(Buffer.isBuffer(pdf.pdf)).to.be.true(); - }); - }); - - if (!process.env.INVITE || !process.env.EMAIL) { - xit('shoud send invitation'); - } else { - it('should send invitation', () => { - return n26.invitations(process.env.EMAIL).then(() => { - console.log('\tInvitation sent'); - }); - }); - } - - if (process.env.NO_TRANSFER || !process.env.TRANSFER_IBAN || !process.env.TRANSFER_BIC || !process.env.TRANSFER_NAME || !process.env.TRANSFER_PIN) { - xit('should transfer money out'); - } else { - it('should transfer money out', () => { - return n26.transfer({ - pin: process.env.TRANSFER_PIN, - iban: process.env.TRANSFER_IBAN, - bic: process.env.TRANSFER_BIC, - amount: 0.01, - name: process.env.TRANSFER_NAME, - reference: 'Test' - }) - .then((t) => { - expect(t).to.have.property('n26Iban'); - expect(t).to.have.property('referenceText', 'Test'); - expect(t).to.have.property('partnerName'); - expect(t).to.have.property('partnerIban'); - expect(t).to.have.property('partnerBic'); - expect(t).to.have.property('partnerAccountIsSepa'); - expect(t).to.have.property('amount'); - expect(t).to.have.deep.property('currencyCode.currencyCode'); - expect(t).to.have.property('linkId'); - expect(t).to.have.property('recurring', false); - expect(t).to.have.property('type', 'DT'); - expect(t).to.have.property('visibleTS'); - expect(t).to.have.property('id'); - - console.log(`\tTransfer: ${t.partnerName} ${t.amount} ${t.partnerBic}`); - }); - }); - } - - if (!process.env.UNPAIR || !process.env.CARD_NUMBER) { - xit('should unpair phone'); - } else { - it('should unpair phone', function (cb) { // eslint-disable-line func-names - this.timeout(60000); - - return n26.unpairInit(process.env.TRANSFER_PIN, process.env.CARD_NUMBER) - .then(() => { - return rl.question('token received by sms: ', (smsNumber) => { - rl.close(); - - return n26.unpairConfirm(smsNumber) - .then(() => { - console.log('\tDevice unpaired'); - cb(); - }) - .catch(cb); - }); - }) - .catch(cb); - }); - } }); diff --git a/tests/unmock/barzahlen.js b/tests/unmock/barzahlen.js new file mode 100644 index 0000000..de9aa49 --- /dev/null +++ b/tests/unmock/barzahlen.js @@ -0,0 +1,69 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +const Number26 = require('../../index'); + +chai.use(dirtyChai); + +const barzahlenProperties = [ + 'depositAllowance', + 'withdrawAllowance', + 'remainingAmountMonth', + 'feeRate', + 'cash26WithdrawalsCount', + 'cash26WithdrawalsSum', + 'atmWithdrawalsCount', + 'atmWithdrawalsSum', + 'monthlyDepositFeeThreshold', + 'success' +]; +const barzahlenBranchesProperties = [ + 'id', + 'lat', + 'lng', + 'title', + 'street_no', + 'zipcode', + 'city', + 'countrycode', + 'opening_hours', + 'logo_url', + 'logo_thumbnail_url', + 'minutes_until_close', + 'offline_partner_id' +]; + +describe('Barzahlen', () => { + it('should check barzahlen', () => { + return global.n26.barzahlen() + .then((barzahlen) => { + barzahlenProperties.forEach(property => { + expect(barzahlen).to.have.deep.property(property); + }); + + console.log(`\tBarzahlen: ${barzahlen.depositAllowance} € deposit allowed`); + }); + }); + + it('should get barzahlen', () => { + return Number26.barzahlen({ + nelat: 52.6078, + nelon: 13.5338, + swlat: 52.4165, + swlon: 13.2688 + }) + .then((barzahlenBranches) => { + barzahlenBranches.forEach((branch) => { + barzahlenBranchesProperties.forEach(property => { + expect(branch).to.have.deep.property(property); + }); + }); + + console.log(`\tBarzahlen: ${barzahlenBranches.length} places in this zone`); + }); + }); +}); diff --git a/tests/unmock/cards.js b/tests/unmock/cards.js new file mode 100644 index 0000000..f61b2a5 --- /dev/null +++ b/tests/unmock/cards.js @@ -0,0 +1,99 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +chai.use(dirtyChai); + +describe('Card', () => { + let card; + let limitOnline; + + before((done) => { + return global.n26.cards() + .then((cards) => { + card = cards.data[0]; + + done(); + }); + }); + + it('should get cards', () => { + return global.n26.cards() + .then((cards) => { + expect(cards).to.be.an('object'); + expect(cards).to.have.deep.property('paging.totalResults'); + expect(cards).to.have.property('data').that.is.an('array'); + + console.log(`\t${cards.paging.totalResults} cards`); + + cards.data.forEach(c => { + expect(c).to.have.property('maskedPan'); + expect(c).to.have.property('expirationDate'); + expect(c).to.have.property('cardType'); + expect(c).to.have.property('n26Status'); + expect(c).to.have.property('pinDefined'); + expect(c).to.have.property('cardActivated'); + expect(c).to.have.property('usernameOnCard'); + expect(c).to.have.property('id'); + + console.log(`\t- ${c.cardType} ${c.n26Status} ${c.maskedPan}`); + }); + }); + }); + + it('should get card limits', () => { + return card.limits() + .then((limits) => { + expect(limits).to.be.an('Array'); + limits.forEach((l) => { + expect(l).to.have.property('limit'); + if (l.limit === 'COUNTRY_LIST') { + expect(l).to.have.property('countryList'); + } else { + expect(l).to.have.property('amount'); + + if (l.limit === 'E_COMMERCE_TRANSACTION') { + limitOnline = l.amount; + } + } + }); + + console.log(`\tCard limits ${limits[0].limit} -> ${limits[0].amount}`); + }); + }); + + it('should set card limits', () => { + return card.limits({ + amount: (limitOnline) ? 0 : 5000, + limit: 'E_COMMERCE_TRANSACTION' + }) + .then((l) => { + console.log(`\tCard set limit ${l.limit}: ${l.amount}`); + + return card.limits({amount: limitOnline, limit: 'E_COMMERCE_TRANSACTION'}); + }); + }); + + it('should block card', () => { + return card.block() + .then(() => { + return global.n26.cards(card.id); + }) + .then((c) => { + console.log(`\tCard n26Status: ${c.n26Status}`); + }); + }); + + it('should unblock card', () => { + return card.unblock() + .then(() => { + return global.n26.cards(card.id); + }) + .then((c) => { + console.log(`\tCard n26Status: ${c.n26Status}`); + }); + }); +}); diff --git a/tests/unmock/index.js b/tests/unmock/index.js new file mode 100644 index 0000000..cf45779 --- /dev/null +++ b/tests/unmock/index.js @@ -0,0 +1,258 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +const Number26 = require('../../index'); + +chai.use(dirtyChai); + +const meProperties = [ + 'userInfo.id', + 'userInfo.email', + 'userInfo.firstName', + 'userInfo.lastName', + 'userInfo.kycFirstName', + 'userInfo.kycLastName', + 'userInfo.title', + 'userInfo.gender', + 'userInfo.birthDate', + 'userInfo.signupCompleted', + 'userInfo.nationality', + 'userInfo.birthPlace', + 'userInfo.mobilePhoneNumber', + 'userInfo.shadowID', + 'account.status', + 'account.availableBalance', + 'account.usableBalance', + 'account.bankBalance', + 'account.iban', + 'account.id', + 'cards[0].maskedPan', + 'cards[0].expirationDate', + 'cards[0].cardType', + 'cards[0].exceetExpressCardDelivery', + 'cards[0].n26Status', + 'cards[0].pinDefined', + 'cards[0].cardActivated', + 'cards[0].id', + 'addresses[0].addressLine1', + 'addresses[0].streetName', + 'addresses[0].houseNumberBlock', + 'addresses[0].zipCode', + 'addresses[0].cityName', + 'addresses[0].countryName', + 'addresses[0].type', + 'addresses[0].id', + 'userFeatures', + 'userStatus.singleStepSignup', + 'userStatus.emailValidationInitiated', + 'userStatus.emailValidationCompleted', + 'userStatus.phonePairingInitiated', + 'userStatus.phonePairingCompleted', + 'userStatus.kycInitiated', + 'userStatus.kycCompleted', + 'userStatus.kycWebIDInitiated', + 'userStatus.kycWebIDCompleted', + 'userStatus.cardActivationCompleted', + 'userStatus.cardIssued', + 'userStatus.pinDefinitionCompleted', + 'userStatus.bankAccountCreationInitiated', + 'userStatus.bankAccountCreationSucceded', + 'userStatus.firstIncomingTransaction', + 'userStatus.smsVerificationCode', + 'userStatus.unpairTokenCreation', + 'userStatus.finoIntegrationStatus', + 'userStatus.id', + 'preference.locale', + 'preference.AAPushNotificationActive', + 'preference.AFPushNotificationActive', + 'preference.AVPushNotificationActive', + 'preference.ARPushNotificationActive', + 'preference.DTPushNotificationActive', + 'preference.CTPushNotificationActive', + 'preference.DDPushNotificationActive', + 'preference.DRPushNotificationActive', + 'preference.AAEmailNotificationActive', + 'preference.AFEmailNotificationActive', + 'preference.AVEmailNotificationActive', + 'preference.AREmailNotificationActive', + 'preference.DTEmailNotificationActive', + 'preference.CTEmailNotificationActive', + 'preference.DDEmailNotificationActive', + 'preference.DREmailNotificationActive', + 'preference.id', + 'userCustomSetting.RATING_DIALOG_SEEN', + 'userCustomSetting.TRANSFERWISE_DIALOG_SEEN', + 'userCustomSetting.OVERDRAFT_NOTIFY_UPGRADE', + 'userCustomSetting.user' +]; +const statusesProperties = [ + 'singleStepSignup', + 'emailValidationInitiated', + 'emailValidationCompleted', + 'phonePairingInitiated', + 'phonePairingCompleted', + 'kycInitiated', + 'kycCompleted', + 'kycWebIDInitiated', + 'kycWebIDCompleted', + 'cardActivationCompleted', + 'cardIssued', + 'pinDefinitionCompleted', + 'bankAccountCreationInitiated', + 'bankAccountCreationSucceded', + 'firstIncomingTransaction', + 'smsVerificationCode', + 'unpairTokenCreation', + 'finoIntegrationStatus', + 'id' +]; +const contactsProperties = [ + 'id', + 'name', + 'subtitle', + 'account.accountType', + 'account.iban', + 'account.bic' +]; + +describe('Create instance', function () { // eslint-disable-line func-names + this.timeout(60000); + + it('should create instance', () => { + return new Number26(global.CONFIG.email, global.CONFIG.password) + .then((m) => { + expect(m).to.be.exist(); + global.n26 = m; + }); + }); + + it('should get profil', () => { + return global.n26.me(true) + .then((profil) => { + meProperties.forEach(property => { + expect(profil).to.have.deep.property(property); + }); + + console.log(`\tMe: ${profil.userInfo.firstName} ${profil.userInfo.lastName}`); + }); + }); + + it('should get account', () => { + return global.n26.account() + .then((account) => { + expect(account).to.have.property('availableBalance'); + expect(account).to.have.property('bankBalance'); + expect(account).to.have.property('iban'); + expect(account).to.have.property('id'); + expect(account).to.have.property('status'); + expect(account).to.have.property('usableBalance'); + + + console.log(`\tAccount: ${account.status} ${account.iban}`); + }); + }); + + it('should get addresses', () => { + return global.n26.addresses() + .then((addresses) => { + expect(addresses).to.be.an('object'); + expect(addresses).to.have.deep.property('paging.totalResults'); + expect(addresses).to.have.property('data').that.is.an('array'); + + console.log(`\t${addresses.paging.totalResults} addresses`); + + addresses.data.forEach(a => { + expect(a).to.have.property('addressLine1'); + expect(a).to.have.property('streetName'); + expect(a).to.have.property('houseNumberBlock'); + expect(a).to.have.property('zipCode'); + expect(a).to.have.property('cityName'); + expect(a).to.have.property('countryName'); + expect(a).to.have.property('type'); + expect(a).to.have.property('id'); + + console.log(`\t- ${a.type} ${a.addressLine1} ${a.streetName}`); + }); + }); + }); + + it('should get recipients', () => { + return global.n26.recipients() + .then((recipients) => { + expect(recipients).to.be.an('array'); + + console.log(`\t${recipients.length} recipients`); + + recipients.forEach(r => { + expect(r).to.have.property('iban'); + expect(r).to.have.property('name'); + expect(r).to.have.property('bic'); + + console.log(`\t- ${r.name} ${r.iban} ${r.bic}`); + }); + }); + }); + + it('should return statuses', () => { + return global.n26.statuses().then((statuses) => { + statusesProperties.forEach(property => { + expect(statuses).to.have.deep.property(property); + }); + + console.log(`\tYour card was actived: ${new Date(statuses.cardActivationCompleted)}`); + }); + }); + + it('should get account limits', () => { + return global.n26.limits().then((limits) => { + console.log(`\tYour account is limited to ${limits[0].amount} for ${limits[0].limit}`); + }); + }); + + it('should set account limits', () => { + let previousAtmDailyAccount; + + return global.n26.limits().then((limits) => { + limits.forEach((limit) => { + if (limit.limit === 'ATM_DAILY_ACCOUNT') { + previousAtmDailyAccount = limit.amount; + } + }); + }) + .then(() => global.n26.limits({atm: 500})) + .then(() => global.n26.limits()) + .then((limits) => { + limits.forEach((limit) => { + if (limit.limit === 'ATM_DAILY_ACCOUNT') { + expect(limit.amount).to.be.eql(500); + } + }); + + return global.n26.limits({atm: previousAtmDailyAccount}); + }); + }); + + it('should get contacts', () => { + return global.n26.contacts().then((contacts) => { + contacts.forEach((contact) => { + contactsProperties.forEach(property => { + expect(contact).to.have.deep.property(property); + }); + }); + + console.log(`\tFirst contacts: ${contacts[0].name} ${contacts[0].subtitle}`); + }); + }); + + require('./cards.js'); + require('./barzahlen.js'); + require('./transactions.js'); + require('./statements.js'); + require('./invitations.js'); + require('./transfer.js'); + require('./unpair.js'); +}); diff --git a/tests/unmock/invitations.js b/tests/unmock/invitations.js new file mode 100644 index 0000000..c36d6b3 --- /dev/null +++ b/tests/unmock/invitations.js @@ -0,0 +1,33 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +chai.use(dirtyChai); + +describe('Invitation', () => { + it('should return email invited', () => { + return global.n26.invitations().then((emails) => { + expect(emails).to.be.an('array'); + emails.forEach((e) => { + ['invited', 'status', 'reward', 'created'].forEach((p) => { + expect(e).to.have.property(p); + }); + }); + + console.log(`\t${emails[0].invited} was invited`); + }); + }); + + if (global.CONFIG.options.indexOf('Invitation') === -1) { + xit('shoud send invitation'); + } else { + it('should send invitation', () => { + return global.n26.invitations(global.CONFIG.inviteEmail).then(() => { + console.log('\tInvitation sent'); + }); + }); + } +}); diff --git a/tests/unmock/statements.js b/tests/unmock/statements.js new file mode 100644 index 0000000..9750be6 --- /dev/null +++ b/tests/unmock/statements.js @@ -0,0 +1,56 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +chai.use(dirtyChai); + +const statementsProperties = [ + 'id', + 'month', + 'url', + 'visibleTS', + 'year' +]; + +describe('Statements', () => { + it('should return statements', () => { + return global.n26.statements().then((statements) => { + statements.forEach((statement) => { + statementsProperties.forEach((property) => { + expect(statement).to.have.deep.property(property); + }); + }); + + console.log(`\tLast statements for ${statements[0].month}/${statements[0].year}`); + }); + }); + + if (global.CONFIG.options.indexOf('Statement') === -1) { + xit('should get last statement file'); + } else { + it('should get last statement file', function () { // eslint-disable-line func-names + this.timeout(25000); + + return global.n26.statements().then((statements) => statements[0].id) + .then(statementId => { + return Promise.all([ + global.n26.statement(statementId), + global.n26.statement(statementId, true) + ]); + }) + .spread((base64, pdf) => { + [base64, pdf].forEach((statement) => { + ['id', 'type', 'pdf'].forEach((property) => { + expect(statement).to.have.deep.property(property); + }); + }); + + expect(base64.pdf).to.be.a('String'); + expect(Buffer.isBuffer(pdf.pdf)).to.be.true(); + }); + }); + } +}); diff --git a/tests/unmock/transactions.js b/tests/unmock/transactions.js new file mode 100644 index 0000000..b03730b --- /dev/null +++ b/tests/unmock/transactions.js @@ -0,0 +1,99 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +chai.use(dirtyChai); + +const commonTransactionFields = ['id', 'userId', 'type', 'amount', 'smartLinkId', 'linkId', 'accountId', 'category', 'cardId', 'pending', 'transactionNature', 'visibleTS', 'recurring']; + +const transactionFields = { + PT: commonTransactionFields.concat(['currencyCode', 'originalAmount', 'originalCurrency', 'exchangeRate', 'merchantCity', 'mcc', 'mccGroup', 'merchantName', 'merchantId']), + DT: commonTransactionFields.concat(['partnerAccountIsSepa', 'partnerName', 'partnerIban', 'referenceText', 'userCertified', 'smartContactId']), + CT: commonTransactionFields.concat(['currencyCode', 'partnerBic', 'partnerAccountIsSepa', 'partnerName', 'partnerIban', 'referenceText', 'smartContactId', 'confirmed']), + AE: commonTransactionFields.concat(['currencyCode', 'originalAmount', 'originalCurrency', 'exchangeRate', 'merchantCity', 'mcc', 'mccGroup', 'merchantName', 'merchantId']), + AA: commonTransactionFields.concat(['currencyCode', 'originalAmount', 'originalCurrency', 'exchangeRate', 'merchantCity', 'mcc', 'mccGroup', 'merchantName', 'merchantId', 'transactionTerminal']) +}; + +const transactionProperties = [ + 'id', + 'userId', + 'type', + 'amount', + 'smartLinkId', + 'currencyCode', + 'originalAmount', + 'originalCurrency', + 'exchangeRate', + 'merchantCity', + 'visibleTS', + 'mcc', + 'mccGroup', + 'merchantName', + 'merchantId', + 'recurring', + 'linkId', + 'accountId', + 'category', + 'cardId', + 'pending', + 'transactionNature', + 'tags' +]; + +describe('Transactions', () => { + it('should get transactions - limit 2', () => { + return global.n26.transactions({limit: 2}) + .then((transactions) => { + expect(transactions).to.be.an('array'); + + console.log(`\tLast ${transactions.length} transactions`); + + transactions.forEach(t => { + expect(t).to.have.property('type'); + + if (!transactionFields[t.type]) { + console.log(t); + } + + transactionFields[t.type].forEach(f => { + expect(t).to.have.property(f); + }); + + console.log(`\t- ${t.type} ${t.amount} ${t.merchantName || t.partnerName}`); + }); + }); + }); + + it('should get transaction detail', () => { + return global.n26.transactions() + .then((transactions) => { + return global.n26.transaction(transactions[0].id).then(detail => { + transactionProperties.forEach(property => { + expect(detail).to.have.deep.property(property); + }); + }); + }); + }); + + it('should update memo on transaction', () => { + return global.n26.transactions() + .then((transactions) => { + return global.n26.transaction(transactions[0].id, {meta: true}).then(d => { + const previousMemo = d.meta.memo; + const newMemo = `YOLO${Math.round(Math.random() * 1000)}`; + + return global.n26.memo(transactions[0].smartLinkId, newMemo) + .then(() => { + return global.n26.transaction(transactions[0].id, {meta: true}).then(dd => { + expect(dd.meta.memo).to.be.eql(newMemo); + + return global.n26.memo(transactions[0].smartLinkId, previousMemo); + }); + }); + }); + }); + }); +}); diff --git a/tests/unmock/transfer.js b/tests/unmock/transfer.js new file mode 100644 index 0000000..abfda6e --- /dev/null +++ b/tests/unmock/transfer.js @@ -0,0 +1,42 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const chai = require('chai'); +const dirtyChai = require('dirty-chai'); +const expect = chai.expect; + +chai.use(dirtyChai); + +describe('Transfer', () => { + if (global.CONFIG.options.indexOf('Transfer') === -1) { + xit('should transfer money out'); + } else { + it('should transfer money out', () => { + return global.n26.transfer({ + pin: global.CONFIG.pin, + iban: global.CONFIG.transferIBAN, + bic: global.CONFIG.transferBIC, + amount: 0.01, + name: global.CONFIG.transferNAME, + reference: 'Test' + }) + .then((t) => { + expect(t).to.have.property('n26Iban'); + expect(t).to.have.property('referenceText', 'Test'); + expect(t).to.have.property('partnerName'); + expect(t).to.have.property('partnerIban'); + expect(t).to.have.property('partnerBic'); + expect(t).to.have.property('partnerAccountIsSepa'); + expect(t).to.have.property('amount'); + expect(t).to.have.deep.property('currencyCode.currencyCode'); + expect(t).to.have.property('linkId'); + expect(t).to.have.property('recurring', false); + expect(t).to.have.property('type', 'DT'); + expect(t).to.have.property('visibleTS'); + expect(t).to.have.property('id'); + + console.log(`\tTransfer: ${t.partnerName} ${t.amount} ${t.partnerBic}`); + }); + }); + } +}); diff --git a/tests/unmock/unpair.js b/tests/unmock/unpair.js new file mode 100644 index 0000000..bf29ed7 --- /dev/null +++ b/tests/unmock/unpair.js @@ -0,0 +1,34 @@ +'use strict'; +/* eslint-disable global-require, max-len, no-console, arrow-body-style */ + +const readline = require('readline'); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +describe('Unpair', () => { + if (global.CONFIG.options.indexOf('Unpair') === -1) { + xit('should unpair phone'); + } else { + it('should unpair phone', function (cb) { // eslint-disable-line func-names + this.timeout(60000); + + return global.n26.unpairInit(global.CONFIG.pin, global.CONFIG.cardNumber) + .then(() => { + return rl.question('token received by sms: ', (smsNumber) => { + rl.close(); + + return global.n26.unpairConfirm(smsNumber) + .then(() => { + console.log('\tDevice unpaired'); + cb(); + }) + .catch(cb); + }); + }) + .catch(cb); + }); + } +}); From 11437d2ebcedf57c02461c2882a547f62c066ddc Mon Sep 17 00:00:00 2001 From: Pierrick Date: Sat, 18 Jun 2016 15:18:29 +0200 Subject: [PATCH 5/6] Fix docs --- lib/card.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/card.js b/lib/card.js index c87ea47..68ea0e2 100644 --- a/lib/card.js +++ b/lib/card.js @@ -32,10 +32,10 @@ class Card { /** * Get / set card limit * - * @property {Object} [limit] - * @property {String} limit.limit Limit type - * @property {Number} [limit.amount] Amount - * @property {String[]} [limit.countryList] Array of country code + * @param {Object} [limit] + * @param {String} limit.limit Limit type + * @param {Number} [limit.amount] Amount + * @param {String[]} [limit.countryList] Array of country code * * @return {Promise} */ @@ -58,7 +58,7 @@ class Card { /** * Block card * - * @return {Promise<>} + * @return {Promise} */ block() { return utils.callApi(this.account, 'blockCard', this.id) @@ -68,7 +68,7 @@ class Card { /** * Unblock card * - * @return {Promise<>} + * @return {Promise} */ unblock() { return utils.callApi(this.account, 'unblockCard', this.id) From e19867aba3e6f5743a9f49fd65997bb28316e149 Mon Sep 17 00:00:00 2001 From: Pierrick Date: Sat, 18 Jun 2016 15:23:11 +0200 Subject: [PATCH 6/6] Bump to 1.1.0 --- CHANGELOG.md | 14 ++++++++++++++ package.json | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6a6d498 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [1.1.0] - 2016-06-18 + +### Added + ++ Get one card ++ Cards are now instance of `Card` ++ Add Set / get limits on card ++ Add Block / unblock on card ++ Better CLI for unmocked tests diff --git a/package.json b/package.json index 2e52b2c..5a1322d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "number26", - "version": "1.0.0", + "version": "1.1.0", "description": "Un-official node.js module for interact with your number26 account", "main": "index.js", "scripts": {