Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
2,392 changes: 2,372 additions & 20 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Tradingview instant stocks API, indicator alerts, trading bot, and more !",
"main": "main.js",
"scripts": {
"test": "node test"
"test": "vitest"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -34,6 +34,7 @@
"@mathieuc/console": "^1.0.1",
"eslint": "^7.25.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1"
"eslint-plugin-import": "^2.22.1",
"vitest": "^0.31.1"
}
}
1 change: 1 addition & 0 deletions src/miscRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ module.exports = {
* @prop {number} notifications.following Notification from following accounts
* @prop {string} session User session
* @prop {string} sessionHash User session hash
* @prop {string} signature User session signature
* @prop {string} privateChannel User private channel
* @prop {string} authToken User auth token
* @prop {Date} joinDate Account creation date
Expand Down
240 changes: 240 additions & 0 deletions tests/allErrors.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import { describe, it, expect } from 'vitest';
import TradingView from '../main';

describe('AllErrors', () => {
const waitForError = (instance: any) => new Promise<string[]>((resolve) => {
instance.onError((...error: string[]) => {
resolve(error);
});
});

it('throws an error when an invalid token is set', async () => {
console.log('Testing "Credentials error" error:');

const client = new TradingView.Client({
token: 'FAKE_CREDENTIALS', // Set wrong credentials
});

const error = await waitForError(client);
console.log('=> Client error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Credentials error:');
expect(error[1]).toBe('Wrong or expired sessionid/signature');
expect(error.length).toBe(2);
});

it('throws an error when an invalid symbol is set', async () => {
console.log('Testing "invalid symbol" error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();
chart.setMarket('XXXXX');

const error = await waitForError(chart);
console.log('=> Chart error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('(ser_1) Symbol error:');
expect(error[1]).toBe('invalid symbol');
expect(error.length).toBe(2);
});

it('throws an error when an invalid timezome is set', async () => {
console.log('Testing "invalid timezone" error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();
chart.setMarket('BINANCE:BTCEUR');

// @ts-expect-error
chart.setTimezone('Nowhere/Nowhere');

const error = await waitForError(chart);
console.log('=> Chart error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Critical error:');
expect(error[1]).toBe('invalid timezone');
expect(error[2]).toBe('method: switch_timezone. args: "[Nowhere/Nowhere]"');
expect(error.length).toBe(3);
});

it('throws an error when a custom timeframe is set without premium', async () => {
console.log('Testing "custom timeframe" error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();

chart.setMarket('BINANCE:BTCEUR', { // Set a market
// @ts-expect-error
timeframe: '20', // Set a custom timeframe
/*
Timeframe '20' isn't available because we are
not logged in as a premium TradingView account
*/
});

const error = await waitForError(chart);
console.log('=> Chart error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Series error:');
expect(error[1]).toBe('custom_resolution');
expect(error.length).toBe(2);
});

it('throws an error when an invalid timeframe is set', async () => {
console.log('Testing "Invalid timeframe" error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();

chart.setMarket('BINANCE:BTCEUR', { // Set a market
// @ts-expect-error
timeframe: 'XX', // Set an invalid timeframe
});

const error = await waitForError(chart);
console.log('=> Chart error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Critical error:');
expect(error[1]).toBe('invalid parameters');
expect(error[2]).toBe('method: create_series. args: "[$prices, s1, ser_1, XX, 100]"');
expect(error.length).toBe(3);
});

it('throws an error when a premium chart type is set without premium', async () => {
console.log('Testing "Study not auth" error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();

chart.setMarket('BINANCE:BTCEUR', { // Set a market
timeframe: '15',
type: 'Renko',
});

const error = await waitForError(chart);
console.log('=> Chart error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Series error:');
expect(error[1]).toBe('study_not_auth:BarSetRenko@tv-prostudies-66');
expect(error.length).toBe(2);
});

it('throws an error when series is edited before market is set', async () => {
console.log('Testing "Set the market before..." error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();

setImmediate(() => {
chart.setSeries('15');
});

const error = await waitForError(chart);
console.log('=> Chart error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Please set the market before setting series');
expect(error.length).toBe(1);
});

it('throws an error when getting a non-existent indicator', async () => {
console.log('Testing "Inexistent indicator" error:');

expect(
TradingView.getIndicator('STD;XXXXXXX'),
).rejects.toThrow('Inexistent or unsupported indicator: "undefined"');
});

it('throws an error when setting an invalid study option value', async () => {
console.log('Testing "Invalid value" error:');

const client = new TradingView.Client();
const chart = new client.Session.Chart();

chart.setMarket('BINANCE:BTCEUR'); // Set a market

const ST = await TradingView.getIndicator('STD;Supertrend');
ST.setOption('Factor', -1); // This will cause an error

const Supertrend = new chart.Study(ST);

const error = await waitForError(Supertrend) as any;
console.log('=> Study error:', error);

expect(error).toEqual([
{
ctx: {
length: -1,
nameInvalidValue: 'factor',
bar_index: 0,
operation: '>',
funName: '\'supertrend\'',
},
error: 'Error on bar {bar_index}: Invalid value of the \'{nameInvalidValue}\' argument ({length}) in the \'{funName}\' function. It must be {operation} 0.',
},
'undefined',
]);

console.log('OK');
});

it('throws an error when getting user data without signature', async () => {
console.log('Testing "Wrong or expired sessionid/signature" error using getUser method:');

if (!process.env.SESSION || !process.env.SIGNATURE) {
console.log('=> Skipping test because SESSION env var is not set');
return;
}

console.log('Trying with signaure');
const userInfo = await TradingView.getUser(process.env.SESSION, process.env.SIGNATURE);

console.log('Result:', {
id: userInfo.id,
username: userInfo.username,
firstName: userInfo.firstName,
lastName: userInfo.lastName,
following: userInfo.following,
followers: userInfo.followers,
notifications: userInfo.notifications,
joinDate: userInfo.joinDate,
});

expect(userInfo).toBeDefined();
expect(userInfo.id).toBeDefined();

console.log('Trying without signaure');
expect(
TradingView.getUser(process.env.SESSION),
).rejects.toThrow('Wrong or expired sessionid/signature');

console.log('OK');
});

it('throws an error when creating an authenticated client without signature', async () => {
console.log('Testing "Wrong or expired sessionid/signature" error using client:');

if (!process.env.SESSION || !process.env.SIGNATURE) {
console.log('=> Skipping test because SESSION env var is not set');
return;
}

const client = new TradingView.Client({
token: process.env.SESSION,
});

const error = await waitForError(client);
console.log('=> Client error:', error);

expect(error).toBeDefined();
expect(error[0]).toBe('Credentials error:');
expect(error[1]).toBe('Wrong or expired sessionid/signature');
expect(error.length).toBe(2);
});
});
115 changes: 115 additions & 0 deletions tests/authenticated.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { describe, it, expect } from 'vitest';
import TradingView from '../main';
import utils from './utils';

describe('Authenticated actions', () => {
it('gets user info', async () => {
console.log('Testing getUser method');

if (!process.env.SESSION || !process.env.SIGNATURE) {
console.log('=> Skipping test because SESSION env var is not set');
return;
}

const userInfo = await TradingView.getUser(
process.env.SESSION as string,
process.env.SIGNATURE,
);

console.log('User:', {
id: userInfo.id,
username: userInfo.username,
firstName: userInfo.firstName,
lastName: userInfo.lastName,
following: userInfo.following,
followers: userInfo.followers,
notifications: userInfo.notifications,
joinDate: userInfo.joinDate,
});

expect(userInfo).toBeDefined();
expect(userInfo.id).toBeDefined();
expect(userInfo.username).toBeDefined();
expect(userInfo.firstName).toBeDefined();
expect(userInfo.lastName).toBeDefined();
expect(userInfo.following).toBeDefined();
expect(userInfo.followers).toBeDefined();
expect(userInfo.notifications).toBeDefined();
expect(userInfo.notifications.following).toBeDefined();
expect(userInfo.notifications.user).toBeDefined();
expect(userInfo.joinDate).toBeDefined();

expect(userInfo.session).toBe(process.env.SESSION);
expect(userInfo.signature).toBe(process.env.SIGNATURE);
});

const userIndicators: any[] = [];

it('gets user indicators', async () => {
console.log('Testing getPrivateIndicators method');

if (!process.env.SESSION || !process.env.SIGNATURE) {
console.log('=> Skipping test because SESSION env var is not set');
return;
}

userIndicators.push(...await TradingView.getPrivateIndicators(process.env.SESSION));
console.log('Indicators:', userIndicators.map((i) => i.name));

expect(userIndicators.length).toBeGreaterThan(0);
});

it('creates a chart with all user indicators', async () => {
console.log('Creating logged client');
const client = new TradingView.Client({
token: process.env.SESSION,
signature: process.env.SIGNATURE,
});

const chart = new client.Session.Chart();

console.log('Setting market to BINANCE:BTCEUR...');
chart.setMarket('BINANCE:BTCEUR', { timeframe: 'D' });

// Limit to 3 indicators for testing
const testedIndicators = userIndicators.slice(0, 3);

const checked = new Set();
async function check(item) {
checked.add(item);
console.log('Checked:', [...checked], `(${checked.size}/${testedIndicators.length + 1})`);
}

chart.onUpdate(async () => {
console.log('Market data:', {
name: chart.infos.pro_name,
description: chart.infos.short_description,
exchange: chart.infos.exchange,
price: chart.periods[0].close,
});

await check(Symbol.for('PRICE'));
});

console.log('Loading indicators...');
for (const indic of testedIndicators) {
const privateIndic = await indic.get();
console.log(`[${indic.name}] Loading indicator...`);

const indicator = new chart.Study(privateIndic);

indicator.onReady(() => {
console.log(`[${indic.name}] Indicator loaded !`);
});

indicator.onUpdate(async () => {
console.log(`[${indic.name}] Last plot:`, indicator.periods[0]);
await check(indic.id);
});
}

while (checked.size < testedIndicators.length + 1) await utils.wait(100);

console.log('All indicators loaded !');
}, 10000);
});
Loading