diff --git a/.gitignore b/.gitignore index 1fa415f..319a083 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,18 @@ Jenkinsfile install.sh uninstall.sh /.github/workflows/ +/testApp/node_modules/ +/testApp/suite/_/assets.js +/testApp/suite/_/binaries.js +/testApp/suite/_/giver.js +/testApp/suite/_/init-tests.js +/testApp/suite/aggregations.js +/testApp/suite/auth.js +/testApp/suite/client.js +/testApp/suite/contracts.js +/testApp/suite/crypto.js +/testApp/suite/deploy-ex.js +/testApp/suite/queries.js +/testApp/suite/run-local.js +/testApp/suite/test-error-messages.js +/testApp/package-lock.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..80ef41d --- /dev/null +++ b/.npmignore @@ -0,0 +1,10 @@ +.DS_Store +.gitignore +.gitattributes +**/.idea/ +node_modules/ +package-lock.json +**/*.so +**/*.a +/.github/workflows/ +testApp/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 04b4178..65981bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Release Notes All notable changes to this project will be documented in this file. +## 0.25.0 - Jul 8, 2020 + +### New +- `testApp` to run main js test suite inside puppeteer +- supports for core contexts + ## 0.19.1 - January 29, 2020 ### Fixed diff --git a/README.md b/README.md index c3a785e..05379e4 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,13 @@ TON Labs SDK Client Library for Web ![npm publish](https://github.com/tonlabs/ton-client-web-js/workflows/npm%20publish/badge.svg) -## Run an example: -* Download and run an example: -``` -$ git clone https://github.com/tonlabs/ton-client-web-js.git -$ cd ton-client-web-js -$ npm install -$ cd example -$ npm install -$ npm run web -``` -* Open http://localhost:4000/ in the browser - -This example connects to net.ton.dev and returns the top 10 accounts holding the most grams (filtered by balance, top to bottom). - +# Using `ton-client-web-js` -## Using ton-client-web-js +## Requirements -### Requirements -You can run ton-client-web-js in a browser. +You can run `ton-client-web-js` in a browser. -In this example, we'll use webpack to manage assets. Since it requires[Node.js](nodejs.org), you’ll need to install the latest version. +In this example, we'll use webpack to manage assets. Since it requires [Node.js](nodejs.org), you’ll need to install the latest version. Let's start with a clean [webpack](https://webpack.js.org/) project: @@ -132,4 +118,4 @@ See [https://docs.ton.dev/86757ecb2/p/92b041-overview](https://docs.ton.dev/8675 --- -Copyright 2018-2020 TON DEV SOLUTIONS LTD. \ No newline at end of file +Copyright 2018-2020 TON DEV SOLUTIONS LTD. diff --git a/example/index.js b/example/index.js deleted file mode 100644 index 161ddc8..0000000 --- a/example/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import { TONClient, setWasmOptions } from 'ton-client-web-js'; - -function debugLog(message) { - document.body.insertAdjacentHTML("beforeend", `

${message}

`); -} - -window.addEventListener('load', () => { - (async () => { - setWasmOptions({ - debugLog, - }); - let createStart = Date.now(); - const client = await TONClient.create({ - servers: ['net.ton.dev'] - }); - debugLog(`Client creation time: ${(Date.now() - createStart)}`); - debugLog(`Client uses binary version: ${await client.config.getVersion()}`); - debugLog(`Client connected to: ${await client.config.data.servers}`); - const queryStart = Date.now(); - const accounts = await client.queries.accounts.query({}, 'id balance(format:DEC)', [{path:'balance', direction:'DESC'}], 10); - debugLog(`Query time: ${(Date.now() - queryStart)}`); - debugLog(`${accounts.map(x => ``).join('')}
${x.id}${x.balance}
`); - debugLog(`Now is: ${new Date()}`); - debugLog(`sha512: ${await client.crypto.sha512({text:'text'})}`); - debugLog(`random: ${await client.crypto.randomGenerateBytes(12)}`); - })(); -}); diff --git a/example/web/server.py b/example/web/server.py deleted file mode 100644 index 0a1e78d..0000000 --- a/example/web/server.py +++ /dev/null @@ -1,14 +0,0 @@ -import SimpleHTTPServer -import SocketServer - -PORT = 8888 - -Handler = SimpleHTTPServer.SimpleHTTPRequestHandler -Handler.extensions_map.update({ - '.wasm': 'application/wasm', -}) - -httpd = SocketServer.TCPServer(("", PORT), Handler) - -print "serving at port", PORT -httpd.serve_forever() diff --git a/install.js b/install.js index d3e3da8..b809437 100644 --- a/install.js +++ b/install.js @@ -4,7 +4,7 @@ const http = require('http'); const zlib = require('zlib'); const {version} = require('./package.json'); -const bv = process.env.TON_SDK_BIN_VERSION || (version).split('.')[0]; +const bv = process.env.TON_SDK_BIN_VERSION || version.split('.')[0]; const root = process.cwd(); const binariesHost = 'sdkbinaries-ws.tonlabs.io'; @@ -69,14 +69,37 @@ function downloadAndGunzip(dest, url) { } +// dev_dl - used when this package installed during CI phase or development process. +// Binaries will be copied from location specified by TC_BIN_SRC environment variable. +function dev_dl(dst, binSrc) { + const srcPath = path.resolve(binSrc, dst); + if (!fs.existsSync(srcPath)) { + process.stdout.write(`Skipping ${dst} from ${srcPath} ...\n`); + return; + } + process.stdout.write(`Copying ${dst} from ${srcPath} ...\n`); + const dstPath = path.resolve(root, dst); + const dstDir = path.dirname(path.resolve(dstPath)); + if (!fs.existsSync(dstDir)) { + fs.mkdirSync(dstDir, { recursive: true }); + } + fs.copyFileSync(srcPath, dstPath); +} + + async function dl(dst, src) { - const dst_path = `${root}/${dst}`; - const src_url = `http://${binariesHost}/${src}.gz`; - process.stdout.write(`Downloading from ${src_url} to ${dst_path} ...`); - await downloadAndGunzip(dst_path, src_url); + if ((process.env.TC_BIN_SRC || '') !== '') { + dev_dl(dst, process.env.TC_BIN_SRC); + return; + } + const dstPath = path.resolve(root, dst); + const srcUrl = `http://${binariesHost}/${src}.gz`; + process.stdout.write(`Downloading from ${srcUrl} to ${dstPath} ...`); + await downloadAndGunzip(dstPath, srcUrl); process.stdout.write('\n'); } + async function main() { await dl('tonclient.wasm', `tonclient_${bv}_wasm`); await dl('index.js', `tonclient_${bv}_wasm_js`); diff --git a/package.json b/package.json index ba0ed1e..72865e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ton-client-web-js", - "version": "0.24.0", + "version": "0.25.0", "description": "TON Client Library for Web", "scripts": { "install": "node install.js" @@ -14,10 +14,10 @@ "author": "TON DEV SOLUTIONS LTD.", "license": "Apache-2.0", "licenses": [ - { - "type": "Apache-2.0", - "url": "http://www.apache.org/licenses/LICENSE-2.0" - } + { + "type": "Apache-2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } ], "repository": { "type": "git", @@ -25,7 +25,7 @@ }, "homepage": "https://ton.dev/node-se", "dependencies": { - "ton-client-js": "^0.24.0" + "ton-client-js": "^0" }, "main": "index.js" } diff --git a/testApp/README.md b/testApp/README.md new file mode 100644 index 0000000..65d0f03 --- /dev/null +++ b/testApp/README.md @@ -0,0 +1,32 @@ +# ton-client-web-js + +TON Labs Client Library for Web Test App + +This project contains the `testApp` that runs the main test suite from `ton-client-js`. + +# Requirements + +Common: +- NodeJs (at least 12) + +# Run Tests + +```shell script +npm install +node prepare-suite.js +node run-suite.js +``` + +# Options + +There are some options for preparing and testing: +- `ton-client-js` package besided of the `ton-client-web-js` folder – if it exists then this local package will be used instead of the npm version (local `ton-client-js` will be packed with `npm pack` and then installed into the `testApp` package). This may be useful for running tests with a non published version of the `ton-client-js`. +- `USE_NODE_SE` and `TON_NETWORK_ADDRESS` env variables – used to specify blockchain network for tests. Must be set before `prepare-suite.js` script execution. +- `TC_BIN_SRC` env variable – can be used to specify a local path of a folder with alternative WASM binaries. Variable must be set before `prepare-suite.js` execution. This may be useful for running tests with a non published version of the core library. The required content for this folder is: + - `tonclient.wasm` + - `index.js` +- `TON-SDK` project besided of the `ton-client-web-js` folder – if it exists and `TC_BIN_SRC` doesn't specified then `TC_BIN_SRC` will be set to build output for the WASM core library. This may be useful for running tests with a non published version of the core library. + + +--- +Copyright 2018-2020 TON DEV SOLUTIONS LTD. diff --git a/example/babel.config.js b/testApp/babel.config.js similarity index 100% rename from example/babel.config.js rename to testApp/babel.config.js diff --git a/testApp/index.js b/testApp/index.js new file mode 100644 index 0000000..4654611 --- /dev/null +++ b/testApp/index.js @@ -0,0 +1,5 @@ +import {startTests} from "./suite/_/run"; + +window.addEventListener('load', () => { + startTests(() => {}); +}); diff --git a/example/package.json b/testApp/package.json similarity index 84% rename from example/package.json rename to testApp/package.json index 4a6c6c5..eb699c8 100644 --- a/example/package.json +++ b/testApp/package.json @@ -1,10 +1,10 @@ { - "name": "ton-client-web-js-example", - "version": "1.0.0", - "description": "TON Client Library for Web - Example", + "name": "ton-client-web-js-test-suite-runner", + "version": "0.25.0", + "description": "TON Client Library for Web - Test Suite Runner", "scripts": { - "nmp install": "npm install", - "web": "webpack-dev-server -d --config webpack.config.js --progress --colors --host 0.0.0.0", + "npm install": "npm install", + "web": "webpack-dev-server -d --config webpack.config.js --progress --colors --host 127.0.0.1", "web:bundle": "webpack -p --config webpack.config.js --progress --colors --display-error-details" }, "keywords": [ @@ -21,9 +21,15 @@ }, "homepage": "https://ton.dev/node-se", "dependencies": { + "error-polyfill": "^0.1.2", + "javascript-biginteger": "^0.9.2", + "jest-lite": "^1.0.0-alpha.4", + "opentracing": "^0.14.4", + "ton-client-js": "^0", "ton-client-web-js": "../" }, "devDependencies": { + "puppeteer": "^4.0.1", "webpack": "4.41.5", "webpack-cli": "3.3.10", "webpack-dev-server": "3.10.1", diff --git a/testApp/prepare-suite.js b/testApp/prepare-suite.js new file mode 100644 index 0000000..0c27459 --- /dev/null +++ b/testApp/prepare-suite.js @@ -0,0 +1,200 @@ +const os = require('os'); +const { spawn } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const srcTestsPath = path.resolve(__dirname, 'node_modules', 'ton-client-js', '__tests__'); +const dstTestsPath = path.resolve(__dirname, 'suite'); +const coreSourcePath = path.resolve(__dirname, '..', '..', 'TON-SDK', 'ton_client'); +const runEnv = { ...process.env }; +if (!runEnv.TC_BIN_SRC && fs.existsSync(coreSourcePath)) { + runEnv.TC_BIN_SRC = path.resolve(coreSourcePath, 'platforms', 'ton-client-web', 'build'); +} + + +function run(name, ...args) { + return new Promise((resolve, reject) => { + try { + const isWindows = os.platform() === 'win32'; + const spawned = isWindows + ? spawn('cmd.exe', ['/c', name].concat(args), { + env: runEnv, + }) + : spawn(name, args, { + env: runEnv, + }); + const errors = []; + const output = []; + + spawned.stdout.on('data', function (data) { + output.push(data); + process.stdout.write(data); + }); + + spawned.stderr.on('data', (data) => { + errors.push(data); + process.stderr.write(data.toString()); + }); + + spawned.on('error', (err) => { + reject(err); + }); + + spawned.on('close', (code) => { + if (code === 0) { + resolve(output.join('')); + } else { + reject(errors.join('')); + } + }); + } catch (error) { + reject(error); + } + }); +} + +function replaceLocalhost(address) { + const ipv4 = Object.values(os.networkInterfaces()) + .reduce((acc, x) => acc.concat(x), []) + .find(x => x.family === 'IPv4' && !x.internal); + const ip = ipv4 && ipv4.address; + return address + .replace(/(.*\D)?0\.0\.0\.0(\D.*)?/g, `$1${ip}$2`) + .replace(/(.*\D)?localhost(\D.*)?/g, `$1${ip}$2`) +} + +function extIs(file, ext) { + return path.extname(file).toLowerCase() === ext; +} + +function getTgzNames() { + return fs.readdirSync(__dirname).filter(x => extIs(x, '.tgz')); +} + +function copyTestSuiteFile(source, targetDir, convert) { + const target = path.resolve(targetDir, path.basename(source)); + const content = convert(source, fs.readFileSync(source)); + if (content !== null && content !== undefined) { + if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); + } + fs.writeFileSync(target, content); + } +} + +function copyTestSuiteFolder(sourceDir, targetDir, convert) { + fs.readdirSync(sourceDir).forEach((file) => { + const source = path.resolve(sourceDir, file); + if (fs.lstatSync(source).isDirectory()) { + copyTestSuiteFolder(source, path.resolve(targetDir, file), convert); + } else { + copyTestSuiteFile(source, targetDir, convert); + } + }); +} + +function replaceSection(filePath, section, content) { + const sectionMatch = new RegExp(`(\/\/${section})[^]*(\/\/${section})`, 'g'); + const script = fs.readFileSync(filePath, { encoding: 'utf8' }).toString() + .replace(sectionMatch, `$1\n${content}\n$2`); + fs.writeFileSync(filePath, script, { encoding: 'utf8' }); + +} + +function rewriteRunScript() { + const imports = []; + fs.readdirSync(srcTestsPath).forEach((file) => { + if (extIs(file, '.js')) { + imports.push(`import ${file.slice(0, -3).replace(/-/g, '_')}_suite from '../${file}';`); + } + }); + + const giverKeysPath = path.resolve(os.homedir(), 'giverKeys.json'); + const giverKeys = fs.existsSync(giverKeysPath) + ? JSON.parse(fs.readFileSync(giverKeysPath, { encoding: 'utf8' })) + : null; + const assets = [ + `export default {`, + ` env: {`, + ` USE_NODE_SE: '${process.env.USE_NODE_SE || 'true'}',`, + ` TON_NETWORK_ADDRESS: '${replaceLocalhost(process.env.TON_NETWORK_ADDRESS || 'http://127.0.0.1:8080')}',`, + ` },`, + ` giverKeys: ${JSON.stringify(giverKeys)},`, + ` contracts: {`, + ]; + const collectContracts = (abiVersion) => { + const dir = path.resolve(srcTestsPath, 'contracts', `abi_v${abiVersion}`); + fs.readdirSync(dir).forEach((file) => { + if (!extIs(file, '.tvc')) { + return; + } + const name = file.slice(0, -4); + const abi = path.resolve(dir, `${name}.abi.json`); + const tvc = path.resolve(dir, `${name}.tvc`); + assets.push( + ` '${name}-${abiVersion}': {`, + ` abi: ${fs.readFileSync(abi, 'utf8').trim().split('\n').join('').split('\t').join('')},`, + ` imageBase64: '${fs.readFileSync(tvc).toString('base64')}',`, + ` },`, + ); + }); + }; + [1, 2].forEach((abiVersion) => { + collectContracts(abiVersion); + }); + assets.push( + ' },', + '};', + ); + replaceSection(path.resolve(dstTestsPath, '_', 'run.js'), 'IMPORTS', imports.join('\n')); + fs.writeFileSync(path.resolve(dstTestsPath, '_', 'assets.js'), assets.join('\n'), { encoding: 'utf8' }); +} + +function copyTestSuite() { + copyTestSuiteFolder(srcTestsPath, dstTestsPath, (srcPath, content) => { + const name = path.basename(srcPath); + const skipFile = !extIs(name, '.js') + || name.toLowerCase() === 'testing-platform.js'; + if (skipFile) { + return null; + } + const converted = content.toString() + .replace(/(from\s+["'])\.\.\/\.\.(\/src\/)/gm, '$1ton-client-js$2') + .replace(/(from\s+["'])\.\.(\/src\/)/gm, '$1ton-client-js$2') + .replace(/(from\s+["'])\.\.\/\.\.(\/types)/gm, '$1ton-client-js$2') + .replace(/(from\s+["'])\.\.(\/types)/gm, '$1ton-client-js$2') + .replace(/_000/gm, '000'); + return Buffer.from(converted, 'utf8'); + }); + rewriteRunScript(); +} + +function removeTgzFiles() { + for (const tgz of getTgzNames()) { + console.log('Remove', tgz); + fs.unlinkSync(path.resolve(__dirname, tgz)); + } +} + +(async () => { + console.log('Save', 'package.json'); + const packageJson = fs.readFileSync(path.resolve(__dirname, 'package.json')); + try { + removeTgzFiles(); + + if (fs.existsSync(path.resolve(__dirname, '..', '..', 'ton-client-js'))) { + await run('npm', 'pack', '../../ton-client-js'); + } + await run('npm', 'pack', '../'); + + const tgzNames = getTgzNames(); + console.log('Install', tgzNames.join(' ')); + await run('npm', 'install', '--force', ...tgzNames); + + removeTgzFiles(); + copyTestSuite(); + } finally { + console.log('Restore', 'package.json'); + fs.writeFileSync(path.resolve(__dirname, 'package.json'), packageJson); + } +})(); diff --git a/testApp/run-suite.js b/testApp/run-suite.js new file mode 100644 index 0000000..98a9cb9 --- /dev/null +++ b/testApp/run-suite.js @@ -0,0 +1,146 @@ +const os = require('os'); +const runEnv = { ...process.env }; +const {spawn} = require('child_process'); +const puppeteer = require('puppeteer'); +const path = require('path'); + +function run(name, args, onOutput, onError) { + return new Promise((resolve, reject) => { + try { + const isWindows = os.platform() === 'win32'; + const spawned = isWindows + ? spawn('cmd.exe', ['/c', name].concat(args), { + env: runEnv, + }) + : spawn(name, args, { + env: runEnv, + }); + const errors = []; + const output = []; + + spawned.stdout.on('data', function (data) { + const text = data.toString(); + output.push(text); + onOutput(text); + }); + + spawned.stderr.on('data', (data) => { + const text = data.toString(); + errors.push(text); + if (onError) { + onError(text); + } else { + process.stderr.write(text); + } + }); + + spawned.on('error', (err) => { + reject(err); + }); + + spawned.on('close', (code) => { + if (code === 0) { + resolve(output.join('')); + } else { + reject(errors.join('')); + } + }); + } catch (error) { + reject(error); + } + }); +} + +function extractReport(expect, line) { + const pos = line.toString().indexOf(expect); + if (pos < 0) { + return null; + } + return JSON.parse(line.substr(pos + expect.length)); +} + +let success = 0; +let failure = 0; + +function onOutputLine(line) { + const startLog = extractReport('[TEST_START]', line); + if (startLog) { + return; + } + const successLog = extractReport('[TEST_SUCCESS]', line); + if (successLog) { + success += 1; + console.log(`✓ ${successLog.name} (${success} / ${failure})`); + return; + } + const failureLog = extractReport('[TEST_FAILURE]', line); + if (failureLog) { + failure += 1; + console.log(`\x1b[0;31m𐄂 ${failureLog.name} (${success} / ${failure}) - ${JSON.stringify(failureLog.error, undefined, ' ')}\x1b[m`); + return; + } + const completeLog = extractReport('[TEST_COMPLETE]', line); + if (completeLog) { + console.log(`---`); + console.log(`success: ${success}`); + console.log(`failure: ${failure}`); + process.exit(failure > 0 ? 1 : 0); + } + console.log(line); +} + +let logText = ''; + +function onTestLog(text) { + if (text.indexOf('\n') < 0) { + logText += text; + return; + } + const lines = (logText + text).split('\n'); + logText = lines[lines.length - 1]; + lines.slice(0, -1).forEach(onOutputLine); +} + +function startWebPackDevServer() { + return new Promise((resolve, reject) => { + run( + path.resolve(__dirname, 'node_modules', '.bin', 'webpack-dev-server'), + ['-d', '--config', 'webpack.config.js', '--progress', '--colors', '--host', '127.0.0.1'], + (text) => { + if (text.includes(': Compiled successfully.')) { + resolve(); + } + }, + () => { + }, + ).catch(reject); + }); +} + +async function main() { + await startWebPackDevServer(); + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + page.on('console', (msg) => { + const text = `${msg.text()}\n`; + onTestLog(text); + if (text.includes('[TEST_COMPLETE]')) { + (async () => { + await page.close(); + await browser.close(); + process.exit(0); + })(); + } + }); + await page.goto('http://localhost:4000', {}); +} + + +(async () => { + try { + await main(); + } catch (error) { + console.log(error); + process.exit(1); + } +})(); diff --git a/testApp/suite/_/run.js b/testApp/suite/_/run.js new file mode 100644 index 0000000..b31288f --- /dev/null +++ b/testApp/suite/_/run.js @@ -0,0 +1,82 @@ +import { BigInteger as bigInt } from 'javascript-biginteger'; +import { tests } from './init-tests'; + +//IMPORTS +import aggregations_suite from '../aggregations.js'; +import auth_suite from '../auth.js'; +import client_suite from '../client.js'; +import contracts_suite from '../contracts.js'; +import crypto_suite from '../crypto.js'; +import deploy_ex_suite from '../deploy-ex.js'; +import queries_suite from '../queries.js'; +import run_local_suite from '../run-local.js'; +import test_error_messages_suite from '../test-error-messages.js'; +//IMPORTS + + +function errorToJson(error) { + const json = {}; + Object.entries(error).forEach(([key, value]) => { + json[key] = value; + }); + if (error.message && !json.message) { + json.message = error.message; + } + if (Object.keys(json).length === 0) { + json.message = error.toString(); + } + return json; +} + +export async function startTests(onStateChange) { + try { + await tests.init(); + jest.setTimeout(300000); + + const state = { + version: await tests.client.config.getVersion(), + passed: 0, + failed: 0, + finished: false, + }; + onStateChange(state); + + jest.addEventHandler((event) => { + if (event.name === 'test_start') { + console.log(`[TEST_START] ${JSON.stringify({ + name: event.test.name, + })}`); + } else if (event.name === 'test_success') { + state.passed += 1; + console.log(`[TEST_SUCCESS] ${JSON.stringify({ + name: event.test.name, + })}`); + } else if (event.name === 'test_failure') { + state.failed += 1; + console.log(`[TEST_FAILURE] ${JSON.stringify({ + name: event.test.name, + error: errorToJson(event.error), + errors: event.test.errors && event.test.errors.map(errorToJson), + })}`); + } else { + return; + } + onStateChange(state); + }); + onStateChange(state); + jest.run().then((results) => { + results.forEach((result) => { + result.errors = result.errors.map((e) => { + return e.toString().replace(/\n\s+at\s+.*/gi, '') + }); + }); + console.log(`[TEST_COMPLETE] ${JSON.stringify(results)}`); + state.finished = true; + onStateChange(state); + }).catch((error) => { + console.log('>>>', error); + }); + } catch (error) { + console.log('>>>', error); + } +} diff --git a/testApp/suite/_/testing-platform.js b/testApp/suite/_/testing-platform.js new file mode 100644 index 0000000..1a0b48c --- /dev/null +++ b/testApp/suite/_/testing-platform.js @@ -0,0 +1,157 @@ +import { initTONClient as initTC } from 'ton-client-web-js'; +import * as jestLite from 'jest-lite'; +import { Buffer as buffer } from 'buffer'; +import * as p1 from 'error-polyfill'; +// import * as p2 from 'react-native-console-time-polyfill'; +import { BigInteger as bigInt } from 'javascript-biginteger'; +import assets from './assets'; + +if (bigInt && !global.BigInt) { + global.BigInt = bigInt; +} +if (buffer && !global.Buffer) { + global.Buffer = buffer; +} + +global.jest = jestLite.jest; +['test', 'expect', 'afterAll', 'afterEach', 'beforeAll', 'beforeEach'].forEach((name) => { + jestLite.jest[name] = jestLite[name]; + global[name] = jestLite[name]; +}); + +['addEventHandler', 'run'].forEach((name) => { + jestLite.jest[name] = jestLite[name]; +}); + +jest.test.each = variants => (title, fn) => variants.forEach((v) => { + jest.test(title.replace(/%i/g, v), () => fn(v)); +}); + +const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); +jest.setTimeout = (timeout) => { + global[testTimeoutSymbol] = timeout; +}; + +jestLite.jest.setTimeout(200000); + +function isBigInt(a) { + return typeof a === 'bigint' || a instanceof BigInt; +} + +function compareBigInt(a, b) { + const bigA = BigInt(a); + const bigB = BigInt(b); + if (typeof bigA !== 'bigint') { + return bigA.compare(bigB); + } + if (bigA < bigB) { + return -1; + } + if (bigA > bigB) { + return 1; + } + return 0; +} + +const bigIntMatchers = { + toBeGreaterThan(received, other) { + return { + pass: compareBigInt(received, other) > 0, + message: () => `${received} must be greater than ${other}`, + }; + }, + toBeGreaterThanOrEqual(received, other) { + return { + pass: compareBigInt(received, other) >= 0, + message: () => `${received} must be greater than or equal to ${other}`, + }; + }, + toBeLessThan(received, other) { + return { + pass: compareBigInt(received, other) < 0, + message: () => `${received} must be less than ${other}`, + } + }, + toBeLessThanOrEqual(received, other) { + return { + pass: compareBigInt(received, other) <= 0, + message: () => `${received} must be less than or equal to ${other}`, + } + }, + toEqual(received, other) { + return { + pass: compareBigInt(received, other) === 0, + message: () => `${received} must be equal to ${other}`, + } + }, + // toBe: null, + // toBeCloseTo: null, + // toBeDefined: null, + // toBeFalsy: null, + // toBeInstanceOf: null, + // toBeNaN: null, + // toBeNull: null, + // toBeTruthy: null, + // toBeUndefined: null, + // toContain: null, + // toContainEqual: null, + // toHaveLength: null, + // toHaveProperty: null, + // toMatch: null, + // toMatchObject: null, + // lastCalledWith: null, + // toBeCalled: null, + // toBeCalledWith: null, + // toHaveBeenCalled: null, + // toHaveBeenCalledTimes: null, + // toHaveBeenCalledWith: null, + // toHaveBeenLastCalledWith: null, + // toThrow: null, + // toThrowError: null, +}; +const defaultExpect = jestLite.jest.expect; +global.expect = jestLite.jest.expect = (received) => { + const wrapped = defaultExpect(received); + if (isBigInt(received)) { + Object.entries(bigIntMatchers).forEach(([name, fn]) => { + wrapped[name] = (...args) => fn(received, ...args); + }) + } + return wrapped; +} + + +// platform + +async function findGiverKeys() { + return assets.giverKeys; +} + +async function writeGiverKeys(keys) { +} + +function createJaegerTracer(endpoint) { + return null; +} + +async function initTONClient(tonClientClass) { + return initTC(tonClientClass); +} + +async function loadContractPackage(name, version) { + const contract = assets.contracts[`${name}-${version}`]; + if (!contract) { + throw new Error(`Contract not found: ${name}-${version}`) + } + return contract; +} + +const env = assets.env; +export { + findGiverKeys, + writeGiverKeys, + createJaegerTracer, + initTONClient, + loadContractPackage, + env, +}; diff --git a/example/web/index.html b/testApp/web/index.html similarity index 100% rename from example/web/index.html rename to testApp/web/index.html diff --git a/example/webpack.config.js b/testApp/webpack.config.js similarity index 96% rename from example/webpack.config.js rename to testApp/webpack.config.js index a918d61..6844bc1 100644 --- a/example/webpack.config.js +++ b/testApp/webpack.config.js @@ -31,9 +31,11 @@ module.exports = { test: /\.jsx?$/, include: [ path.resolve('index.js'), + path.resolve(__dirname, './suite'), path.resolve(__dirname, './node_modules/webpack-dev-server'), path.resolve(__dirname, './node_modules/ton-client-js'), path.resolve(__dirname, './node_modules/ton-client-web-js'), + path.resolve(__dirname, './node_modules/jest-lite'), ], loader: 'babel-loader', query: {