From c30d3a80e8cceccb1620ccb19028527209dff43a Mon Sep 17 00:00:00 2001 From: Stefanie Doll Date: Wed, 15 May 2019 11:50:28 +0200 Subject: [PATCH] Added derive e2e tests and documentation (#898) * export IExtrinsic * Switch to localhos & enable tests in promise.spec * Derive.accounts docs (copy from my other branch) + code examples * Fixed existing accounts tests and added tests for all accounts.derive methods * e2e tests and docs/ examples for balances.all and balances.fees * Removed emty lines below jsdoc info * tests & documentation dor derive.chain * Addressed comments from former PR https://github.com/polkadot-js/api/pull/869 * Removed unused import * obseralbe -> observable * undo keith' commit * use correct jsdocs syntax * Re-enable skip --- .../api-derive/src/accounts/idAndIndex.ts | 13 + packages/api-derive/src/accounts/idToIndex.ts | 14 ++ packages/api-derive/src/accounts/indexToId.ts | 13 + packages/api-derive/src/accounts/indexes.ts | 12 +- packages/api-derive/src/balances/all.ts | 16 ++ packages/api-derive/src/balances/fees.ts | 13 + packages/api-derive/src/chain/bestNumber.ts | 3 +- .../src/chain/bestNumberFinalized.ts | 4 +- .../api-derive/src/chain/bestNumberLag.ts | 2 + packages/api-derive/src/chain/getHeader.ts | 6 +- .../api-derive/src/chain/subscribeNewHead.ts | 4 +- packages/api-derive/test/e2e/promise.spec.ts | 2 +- packages/api-derive/test/e2e/rx.spec.ts | 226 +++++++++++++++--- packages/types/README.md | 17 +- 14 files changed, 295 insertions(+), 50 deletions(-) diff --git a/packages/api-derive/src/accounts/idAndIndex.ts b/packages/api-derive/src/accounts/idAndIndex.ts index cbce7d654d..b55f1566cc 100644 --- a/packages/api-derive/src/accounts/idAndIndex.ts +++ b/packages/api-derive/src/accounts/idAndIndex.ts @@ -15,6 +15,19 @@ import { drr } from '../util/drr'; export type AccountIdAndIndex = [AccountId?, AccountIndex?]; +/** + * @name idAndIndex + * @param {(Address | AccountId | AccountIndex | string | null)} address - An accounts address in various formats. + * @description An array containing the [[AccountId]] and [[AccountIndex]] as optional values. + * @example + *
+ * + * ```javascript + * api.derive.accounts.idAndIndex('F7Hs', ([id, ix]) => { + * console.log(`AccountId #${id} with corresponding AccountIndex ${ix}`); + * }); + * ``` + */ export function idAndIndex (api: ApiInterface$Rx) { return (address?: Address | AccountId | AccountIndex | string | null): Observable => { try { diff --git a/packages/api-derive/src/accounts/idToIndex.ts b/packages/api-derive/src/accounts/idToIndex.ts index 7fd3a9fbd1..5eff8c46b1 100644 --- a/packages/api-derive/src/accounts/idToIndex.ts +++ b/packages/api-derive/src/accounts/idToIndex.ts @@ -10,6 +10,20 @@ import { AccountId, AccountIndex } from '@polkadot/types'; import { indexes, AccountIndexes } from './indexes'; import { drr } from '../util/drr'; +/** + * @name idToIndex + * @param {( AccountId | string )} accountId - An accounts Id in different formats. + * @returns Returns the corresponding AccountIndex. + * @example + *
+ * + * ```javascript + * const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; + * api.derive.accounts.idToIndex(ALICE, (accountIndex) => { + * console.log(`The AccountIndex of ${ALICE} is ${accountIndex}`); + * }); + * ``` + */ export function idToIndex (api: ApiInterface$Rx) { return (accountId: AccountId | string): Observable => indexes(api)() diff --git a/packages/api-derive/src/accounts/indexToId.ts b/packages/api-derive/src/accounts/indexToId.ts index 11219fadcb..36bc39cb5a 100644 --- a/packages/api-derive/src/accounts/indexToId.ts +++ b/packages/api-derive/src/accounts/indexToId.ts @@ -10,6 +10,19 @@ import { AccountId, AccountIndex, Vector } from '@polkadot/types'; import { drr } from '../util/drr'; +/** + * @name indexToId + * @param {( AccountIndex | string )} accountIndex - An accounts index in different formats. + * @returns Returns the corresponding AccountId. + * @example + *
+ * + * ```javascript + * api.derive.accounts.indexToId('F7Hs', (accountId) => { + * console.log(`The AccountId of F7Hs is ${accountId}`); + * }); + * ``` + */ export function indexToId (api: ApiInterface$Rx) { return (_accountIndex: AccountIndex | string): Observable => { const querySection = api.query.indices || api.query.balances; diff --git a/packages/api-derive/src/accounts/indexes.ts b/packages/api-derive/src/accounts/indexes.ts index 913b15fdf3..6014de8ad5 100644 --- a/packages/api-derive/src/accounts/indexes.ts +++ b/packages/api-derive/src/accounts/indexes.ts @@ -15,9 +15,19 @@ export type AccountIndexes = { [index: string]: AccountIndex }; const enumsetSize = ENUMSET_SIZE.toNumber(); /** - * Returns all the indexes on the system - this is an unwieldly query since it loops through + * @name indexes + * @returns Returns all the indexes on the system. + * @description This is an unwieldly query since it loops through * all of the enumsets and returns all of the values found. This could be up to 32k depending * on the number of active accounts in the system + * @example + *
+ * + * ```javascript + * api.derive.accounts.indexes((indexes) => { + * console.log('All existing AccountIndexes', indexes); + * }); + * ``` */ export function indexes (api: ApiInterface$Rx) { return (): Observable => { diff --git a/packages/api-derive/src/balances/all.ts b/packages/api-derive/src/balances/all.ts index fc47f1ca19..e15f2734c6 100644 --- a/packages/api-derive/src/balances/all.ts +++ b/packages/api-derive/src/balances/all.ts @@ -14,6 +14,22 @@ import { drr } from '../util/drr'; const EMPTY_ACCOUNT = new AccountId(); +/** + * @name all + * @param {( ccountIndex | AccountId | Address | string )} address - An accounts Id in different formats. + * @returns An object containing the combined results of the storage queries for + * all relevant fees as declared in the substrate chain spec. + * @example + *
+ * + * ```javascript + * const ALICE = 'F7Hs'; + * + * api.derive.balances.all(ALICE, ([accountId, lockedBalance]) => { + * console.log(`The account ${accountId} has a locked balance ${lockedBalance} units.`); + * }); + * ``` + */ export function all (api: ApiInterface$Rx) { return (address: AccountIndex | AccountId | Address | string): Observable => { return idAndIndex(api)(address).pipe( diff --git a/packages/api-derive/src/balances/fees.ts b/packages/api-derive/src/balances/fees.ts index 044d0f9705..313a7c929e 100644 --- a/packages/api-derive/src/balances/fees.ts +++ b/packages/api-derive/src/balances/fees.ts @@ -10,6 +10,19 @@ import BN from 'bn.js'; import { DerivedFees } from '../types'; import { drr } from '../util/drr'; +/** + * @name fees + * @returns An object containing the combined results of the storage queries for + * all relevant fees as declared in the substrate chain spec. + * @example + *
+ * + * ```javascript + * api.derive.balances.fees(([creationFee, transferFee]) => { + * console.log(`The fee for creating a new account on this chain is ${transferFee} units. The fee required for making a transfer is ${transferFee} units.`); + * }); + * ``` + */ export function fees (api: ApiInterface$Rx) { return (): Observable => { return (api.queryMulti([ diff --git a/packages/api-derive/src/chain/bestNumber.ts b/packages/api-derive/src/chain/bestNumber.ts index 3eb0375b61..8216e25ebd 100644 --- a/packages/api-derive/src/chain/bestNumber.ts +++ b/packages/api-derive/src/chain/bestNumber.ts @@ -10,7 +10,8 @@ import { BlockNumber, Header } from '@polkadot/types'; import { drr } from '../util/drr'; /** - * @description Get the latest block number. + * @name bestNumber + * @returns The latest block number. * @example *
* diff --git a/packages/api-derive/src/chain/bestNumberFinalized.ts b/packages/api-derive/src/chain/bestNumberFinalized.ts index 8a931b9b76..eaad5ae1ae 100644 --- a/packages/api-derive/src/chain/bestNumberFinalized.ts +++ b/packages/api-derive/src/chain/bestNumberFinalized.ts @@ -10,8 +10,10 @@ import { BlockNumber, Header } from '@polkadot/types'; import { drr } from '../util/drr'; /** + * @name bestNumberFinalized + * @returns A BlockNumber * @description Get the latest finalized block number. - * example + * @example *
* * ```javascript diff --git a/packages/api-derive/src/chain/bestNumberLag.ts b/packages/api-derive/src/chain/bestNumberLag.ts index ab1731d63f..36f2f63881 100644 --- a/packages/api-derive/src/chain/bestNumberLag.ts +++ b/packages/api-derive/src/chain/bestNumberLag.ts @@ -12,6 +12,8 @@ import { bestNumber } from './bestNumber'; import { bestNumberFinalized } from './bestNumberFinalized'; /** + * @name bestNumberLag + * @returns A number of blocks * @description Calculates the lag between finalized head and best head * @example *
diff --git a/packages/api-derive/src/chain/getHeader.ts b/packages/api-derive/src/chain/getHeader.ts index 5932197175..eb8f1fad72 100644 --- a/packages/api-derive/src/chain/getHeader.ts +++ b/packages/api-derive/src/chain/getHeader.ts @@ -11,8 +11,10 @@ import { drr } from '../util/drr'; import { HeaderAndValidators } from './subscribeNewHead'; /** - * @description Get the a specific block header and extend it with the author - * @param hash: Uint8Array | string + * @name bestNumberFinalized + * @param {( Uint8Array | string )} hash - A block hash as U8 array or string. + * @returns An array containing the block header and the block author + * @description Get a specific block header and extend it with the author * @example *
* diff --git a/packages/api-derive/src/chain/subscribeNewHead.ts b/packages/api-derive/src/chain/subscribeNewHead.ts index 0fd3d37ede..a4f7a6bb8d 100644 --- a/packages/api-derive/src/chain/subscribeNewHead.ts +++ b/packages/api-derive/src/chain/subscribeNewHead.ts @@ -12,7 +12,9 @@ import { drr } from '../util/drr'; export type HeaderAndValidators = [Header, Array]; /** - * @description Subscribe to block headers and extend it with the author + * @name subscribeNewHead + * @returns An array containing the block header and the block author + * @description An observable of the current block header and it's author * @example *
* diff --git a/packages/api-derive/test/e2e/promise.spec.ts b/packages/api-derive/test/e2e/promise.spec.ts index 96f8a909de..d213172191 100644 --- a/packages/api-derive/test/e2e/promise.spec.ts +++ b/packages/api-derive/test/e2e/promise.spec.ts @@ -32,7 +32,7 @@ describe.skip('derive e2e', () => { it('subscribes to newHead, retrieving the actual validator', (done) => { return api.derive.chain.subscribeNewHead(({ author }) => { - console.error('author', author.toString()); + console.log('author', author.toString()); if (author) { done(); diff --git a/packages/api-derive/test/e2e/rx.spec.ts b/packages/api-derive/test/e2e/rx.spec.ts index 8c0e9f745c..95d4ab3f00 100644 --- a/packages/api-derive/test/e2e/rx.spec.ts +++ b/packages/api-derive/test/e2e/rx.spec.ts @@ -3,14 +3,19 @@ // of the Apache-2.0 license. See the LICENSE file for details. import BN from 'bn.js'; + import ApiRx from '@polkadot/api/rx/Api'; import { ApiInterface$Rx } from '@polkadot/api/types'; -import { BlockNumber } from '@polkadot/types'; +import { AccountId, AccountIndex, Balance, BlockNumber } from '@polkadot/types'; import { WsProvider } from '@polkadot/rpc-provider'; const WS_LOCAL = 'ws://127.0.0.1:9944/'; // const WS_POC3 = 'wss://poc3-rpc.polkadot.io/'; +// Dev account Alice +const ID = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; +const IX = 'F7Hs'; + describe.skip('derive e2e', () => { let api: ApiInterface$Rx; @@ -23,49 +28,192 @@ describe.skip('derive e2e', () => { done(); }); - it('derive.chain.bestNumber', async (done) => { - api.derive.chain.bestNumber().subscribe((blockNumber) => { - expect(blockNumber instanceof BlockNumber).toBe(true); - expect((blockNumber as BlockNumber).gten(0)).toBe(true); - done(); - }); - }); - - it('derive.session.sessionProgress', async (done) => { - api.derive.session.sessionProgress().subscribe((progress) => { - expect(progress instanceof BN).toBe(true); - done(); - }); - }); - - it('returns the intentions with balances', async (done) => { - api.derive.staking.intentionsBalances().subscribe((balances) => { - expect(Object.keys(balances as object)).not.toHaveLength(0); - done(); - }); - }); - - // these only work on localhost, not the poc-3 URL + // These derive.accounts tests only work on localhost, not the poc-3 URL // (and it is assuming it sent at least 1 tx) - describe('accounts', () => { - const ID = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; - const IX = 'F7Gh'; + describe('derive.accounts', () => { - it('looks up id & index from id', async (done) => { - // @ts-ignore silence warning until we have static types here - api.derive.accounts.idAndIndex(ID).subscribe(([id, ix]) => { - expect(id.toString()).toEqual(ID); - expect(ix.toString()).toEqual(IX); - done(); + describe('idAndIndex', () => { + it('looks up AccountId & AccountIndex from AccountId', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.idAndIndex(ID).subscribe(([accountId, accountIndex]) => { + expect(accountId.toString()).toEqual(ID); + // The first emitted value for ix is undefined when passing the ID + if (accountIndex) { + expect(accountIndex.toString()).toEqual(IX); + } else { + expect(accountIndex).toEqual(undefined); + } + done(); + }); + }); + + it('looks up AccountId & AccountIndex from AccountIndex', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.idAndIndex(IX).subscribe(([accountId, accountIndex]) => { + // The first emitted value for id is undefined when passing the IX + if (accountId) { + expect(accountId.toString()).toEqual(ID); + } else { + expect(accountId).toEqual(undefined); + } + expect(accountIndex.toString()).toEqual(IX); + done(); + }); }); }); - it('looks up id & index from index', async (done) => { - // @ts-ignore silence warning until we have static types here - api.derive.accounts.idAndIndex(IX).subscribe(([id, ix]) => { - expect(id.toString()).toEqual(ID); - expect(ix.toString()).toEqual(IX); - done(); + describe('indexToId', () => { + it('looks up AccountId from AccountIndex', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.indexToId(IX).subscribe((accountId) => { + // The first emitted value for accountId is undefined when passing the IX + if (accountId) { + expect(accountId instanceof AccountId).toBe(true); + expect(accountId.toString()).toEqual(ID); + } else { + expect(accountId).toEqual(undefined); + } + done(); + }); + }); + }); + + describe('idToIndex', () => { + it('looks up AccountIndex from AccountId', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.idToIndex(ID).subscribe((accountIndex) => { + // The first emitted value for AccountIndex is undefined when passing the ID + if (accountIndex) { + expect(accountIndex instanceof AccountIndex).toBe(true); + expect(accountIndex.toString()).toEqual(IX); + } else { + expect(accountIndex).toEqual(undefined); + } + done(); + }); + }); + }); + + describe('indexes', () => { + it('looks up all AccountIndexes', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.indexes().subscribe((accountIndexes) => { + // A local dev chain should have the AccountIndex of Alice + expect(accountIndexes).toHaveProperty( + ID, + new AccountIndex(IX) + ); + done(); + }); + }); + }); + }); + + // these only work on localhost, not the poc-3 URL + // (and it is assuming it sent at least 1 tx) + describe('derive.balances', () => { + describe('all', () => { + it('It returns an object with all relevant balance information of an account', async (done) => { + api.derive.balances.all(ID).subscribe((balances) => { + expect(balances).toEqual(expect.objectContaining({ + accountId: expect.any(AccountId), + availableBalance: expect.any(Balance), + freeBalance: expect.any(Balance), + lockedBalance: expect.any(Balance), + reservedBalance: expect.any(Balance), + vestedBalance: expect.any(Balance), + votingBalance: expect.any(Balance) + })); + done(); + }); + }); + }); + + describe('fees', () => { + it('fees: It returns an object with all relevant fees of type BN', async (done) => { + api.derive.balances.fees().subscribe((fees) => { + expect(fees).toEqual(expect.objectContaining({ + creationFee: expect.any(BN), + existentialDeposit: expect.any(BN), + transactionBaseFee: expect.any(BN), + transactionByteFee: expect.any(BN), + transferFee: expect.any(BN) + })); + done(); + }); + }); + }); + }); + + describe('derive.chain', () => { + describe('bestNumber', () => { + it('Get the latest block number', async (done) => { + api.derive.chain.bestNumber().subscribe((blockNumber) => { + expect(blockNumber instanceof BlockNumber).toBe(true); + expect((blockNumber as BlockNumber).gten(0)).toBe(true); + done(); + }); + }); + }); + + describe('bestNumberFinalized', () => { + it('Get the latest finalised block number', async (done) => { + api.derive.chain.bestNumberFinalized().subscribe((blockNumber) => { + expect(blockNumber instanceof BlockNumber).toBe(true); + expect((blockNumber as BlockNumber).gten(0)).toBe(true); + done(); + }); + }); + }); + + describe('bestNumberLag', () => { + it('lag between finalised head and best head', async (done) => { + api.derive.chain.bestNumberLag().subscribe((numberLag) => { + expect(numberLag instanceof BlockNumber).toBe(true); + expect((numberLag as BlockNumber).gten(0)).toBe(true); + done(); + }); + }); + }); + + describe('getHeader', () => { + it('gets a specific block header and extended with it\`s author', async (done) => { + api.derive.chain.getHeader().subscribe((headerExtended) => { + // WIP + expect(headerExtended).toEqual(expect.arrayContaining([])); + done(); + }); + }); + }); + + describe('subscribeNewHead', () => { + it('gets an observable of the current block header and it\'s author', async (done) => { + api.derive.chain.subscribeNewHead().subscribe((headerExtended) => { + // WIP + done(); + }); + }); + }); + }); + + describe('derive.session', () => { + describe('sessionProgress', () => { + it('derive.session.sessionProgress', async (done) => { + api.derive.session.sessionProgress().subscribe((progress) => { + expect(progress instanceof BN).toBe(true); + done(); + }); + }); + }); + }); + + describe('derive.staking', () => { + describe('intentionsBalances', () => { + it('returns the intentions with balances', async (done) => { + api.derive.staking.intentionsBalances().subscribe((balances) => { + expect(Object.keys(balances as object)).not.toHaveLength(0); + done(); + }); }); }); }); diff --git a/packages/types/README.md b/packages/types/README.md index eae3dd08fa..ba080e809b 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -8,7 +8,7 @@ On the Rust side, the codec types and primitive types are implemented via the [p These are the base types of the codec. They are typically not used directly, but rather inherited from to create specific types. They are the building blocks for declaring custom types: -| Type | Description | +| **Types** | | | --- | --- | | [[AbstractArray]] | Manages codec arrays. It is an extension to Array | | [[Base]] | A type extends the Base class, when it holds a value | @@ -31,7 +31,7 @@ These are the base types of the codec. They are typically not used directly, but These primitive types are available: -| Type | Description | +| **Types** | | | --- | --- | | [[Bool]] | Representation for a boolean value in the system | | [[Bytes]] | A Bytes wrapper for `Vec` | @@ -65,7 +65,7 @@ These primitive types are available: These custom types implement specific types that are found as part of the Substrate core. They're all extensions of one of the codec types: -| Type | Description | +| **Types** | | | --- | --- | | [[AccountId]] | A wrapper around an AccountId/PublicKey representation | | [[AccountIndex]] | A wrapper around an AccountIndex, which is a shortened, variable-length encoding for an Account | @@ -147,7 +147,7 @@ These custom types implement specific types that are found as part of the Substr These types are not used in the runtime, but are rather used in RPC results: -| Type | Description | +| **Types** | | | --- | --- | | [[ChainProperties]] | Wraps the properties retrieved from the chain via the `system.properties` RPC call | | [[ExtrinsicStatus]] | An EnumType that indicates the status of the Extrinsic as been submitted | @@ -160,3 +160,12 @@ These types are not used in the runtime, but are rather used in RPC results: | [[RuntimeVersion]] | A [[Tuple]] that conatins the [[ApiId]] and [[U32]] version | | [[SignedBlock]] | A [[Block]] that has been signed and contains a [[Justification]] | | [[StorageChangeSet]] | A set of storage changes. It contains the [[Block]] hash and a list of the actual changes | + + +## Derive types + +These types are are specific for the Polkadot-JS API, so you won't find a representation of them in the SCALE codec or the Substrate core. They are used in the [api-derive](https://www.npmjs.com/package/@polkadot/api-derive) methods. + +| **Types** | | +| --- | --- | +| [[HeaderExtended]] | A [[Block]] header with an additional `author` field that indicates the block author] |