diff --git a/.changeset/rare-starfishes-mate.md b/.changeset/rare-starfishes-mate.md new file mode 100644 index 00000000..afb6b3cc --- /dev/null +++ b/.changeset/rare-starfishes-mate.md @@ -0,0 +1,5 @@ +--- +"flow-js-testing": minor +--- + +Dynamically select ports for emulator instead of supplying admin port statically through emulator.start arguments, deprecate use of this argument diff --git a/README.md b/README.md index 3f540b35..49e37b62 100644 --- a/README.md +++ b/README.md @@ -6,14 +6,14 @@

Test your Flow applications written in Cadence with ease
- Quick Intro» + Read the docs

Report Bug · Contribute · - Installation + Installation

@@ -34,15 +34,9 @@ Most of the methods will not work, unless you have Flow Emulator running in the You can install it alongside Flow CLI. Please refer to [Install Flow CLI](https://docs.onflow.org/flow-cli/install) for instructions. -If you have it already installed, run the `flow init` in your terminal to create `flow.json` config file. -Then start the emulator with `flow emulator -v`. +If you have it already installed, run the `flow init` in your terminal to create `flow.json` config file in the root directory of your tests. -## Documentation - -- [Installation](/docs/install.md) -- [API](/docs/api.md) -- Extra Examples - - [Metadata](/docs/examples/metadata.md) +In order to use the emulator within your tests, please refer to the [documentation](https://docs.onflow.org/flow-js-testing/emulator/). ## Playground Integration diff --git a/TRANSITIONS.md b/TRANSITIONS.md new file mode 100644 index 00000000..1f510c03 --- /dev/null +++ b/TRANSITIONS.md @@ -0,0 +1,12 @@ +# Transitions + +## 0001 Deprecate `emulator.start()` port argument + +- **Date:** Jun 28 2022 +- **Type:** Depreaction of `port` argument for `emulator.start()` + +`emulator.start` was previously called with the arguments: `emulator.start(port, options = {})`. The `port` argument has now been removed and manual specification of the ports is no longer recommended. + +However, the `adminPort`, `restPort`, and `grpcPort` of the emulator may be overriden as fields in `options` (i.e. `options.restPort = 1234`) if absolutely necessary - however their use is not advisable and may cause unintended consequences. + +Instead, it is recommended omit supplying a static a port and allow flow-js-testing to automatically determine available ports to supply the emululator. Flow-js-testing will automatically configure @onflow/fcl to use these ports for all of its functionality. diff --git a/dev-test/deploy.test.js b/dev-test/deploy.test.js index 4ead3ec1..3510cdc8 100644 --- a/dev-test/deploy.test.js +++ b/dev-test/deploy.test.js @@ -19,9 +19,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; await init(basePath); - return emulator.start(port); + return emulator.start(); }); // Stop emulator, so it could be restarted diff --git a/dev-test/imports.test.js b/dev-test/imports.test.js index 078e93fd..e04fb688 100644 --- a/dev-test/imports.test.js +++ b/dev-test/imports.test.js @@ -16,7 +16,7 @@ describe("import resolver", () => { const basePath = path.resolve(__dirname, "./cadence"); const port = 8081; await init(basePath, { port }); - return emulator.start(port, false); + return emulator.start(); }); // Stop emulator, so it could be restarted diff --git a/dev-test/interaction.test.js b/dev-test/interaction.test.js index fc399e00..3aeec38a 100644 --- a/dev-test/interaction.test.js +++ b/dev-test/interaction.test.js @@ -17,9 +17,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8082; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -132,9 +131,8 @@ describe("interactions - executeScript", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port, false); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -167,7 +165,7 @@ describe("interactions - executeScript", () => { }); test("executeScript - shall pass with short notation", async () => { - const [result,err] = await shallResolve(executeScript("log-message")); + const [result, err] = await shallResolve(executeScript("log-message")); expect(err).toBe(null); expect(result).toBe(42); }); @@ -191,8 +189,8 @@ describe("interactions - executeScript", () => { } `; const args = [[]]; - return executeScript({ code, args }); - }) + return executeScript({ code, args }); + }); expect(err).toBe(null); expect(result.length).toBe(0); }); diff --git a/dev-test/metadata.test.js b/dev-test/metadata.test.js index 64e9eca0..baa6c5bf 100644 --- a/dev-test/metadata.test.js +++ b/dev-test/metadata.test.js @@ -6,9 +6,8 @@ jest.setTimeout(10000); describe("metadata examples", () => { beforeEach(async () => { const basePath = path.resolve("./cadence"); - const port = 8083; await init(basePath); - return emulator.start(port); + return emulator.start(); }); afterEach(async () => { diff --git a/dev-test/usage.test.js b/dev-test/usage.test.js index b47266c2..c6bd3b7b 100644 --- a/dev-test/usage.test.js +++ b/dev-test/usage.test.js @@ -23,9 +23,8 @@ describe("Basic Usage test", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8084; await init(basePath); - return emulator.start(port); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -80,9 +79,8 @@ describe("Basic Usage test", () => { describe("jest methods", () => { beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8082; await init(basePath); - return emulator.start(port); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -146,10 +144,9 @@ describe("jest methods", () => { describe("Path arguments", () => { beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8082; await init(basePath); - return emulator.start(port, true); + return emulator.start(); }); // Stop emulator, so it could be restarted diff --git a/dev-test/utilities.test.js b/dev-test/utilities.test.js index 698b56d6..4da94dc0 100644 --- a/dev-test/utilities.test.js +++ b/dev-test/utilities.test.js @@ -24,9 +24,8 @@ describe("block height offset", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const base = path.resolve(__dirname, "../cadence"); - const port = 8085; - await init({ base },); - return emulator.start(port); + await init({ base }); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -59,7 +58,6 @@ describe("block height offset", () => { const [offSet] = await getBlockOffset({ addressMap }); expect(offSet).toBe(0); - }); it("should update offset with utility method", async () => { @@ -87,9 +85,8 @@ describe("block height offset utilities", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const base = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init({ base }, { port }); - return emulator.start(port); + await init({ base }); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -118,9 +115,8 @@ describe("dev tests", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const base = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init({ base }, { port }); - return emulator.start(port, false); + await init({ base }); + return emulator.start(); }); // Stop emulator, so it could be restarted diff --git a/docs/README.md b/docs/README.md index b82f8850..a3053046 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ # Flow Javascript Testing Framework Documentation This directory contains the source files for the Flow Javascript Testing Framework documentation. -Read the full version on the [Flow documentation website](https://docs.onflow.org/flow-cli). \ No newline at end of file +Read the full version on the [Flow documentation website](https://docs.onflow.org/flow-js-testing/). diff --git a/docs/api.md b/docs/api.md index b374e123..a3d630a3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -19,22 +19,21 @@ Resolves name alias to a Flow address (`0x` prefixed) under the following condit #### Returns -| Type | Description | -| --------------------------------------------------- | ---------------------------------------- | +| Type | Description | +| ------------------------------------------------------------- | ---------------------------------------- | | [Address](https://docs.onflow.org/fcl/reference/api/#address) | `0x` prefixed address of aliased account | #### Usage ```javascript -import path from "path" +import path from "path"; import { init, emulator, getAccountAddress } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const Alice = await getAccountAddress("Alice"); console.log({ Alice }); @@ -53,18 +52,18 @@ Deploys contract code located inside a Cadence file. Returns the transaction res Props object accepts following fields: -| Name | Type | Optional | Description | -| ------------ | --------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | | name of the file in `contracts` folder (with `.cdc` extension) and name of the contract (please note those should be the same) | +| Name | Type | Optional | Description | +| ------------ | ------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | string | | name of the file in `contracts` folder (with `.cdc` extension) and name of the contract (please note those should be the same) | | `to` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | ✅ | (optional) account address, where contract will be deployed. If this is not specified, framework will create new account with randomized alias. | -| `addressMap` | [AddressMap](#addressmap) | ✅ | (optional) object to use for address mapping of existing deployed contracts | -| `args` | [Any] | ✅ | (optional) arguments, which will be passed to contract initializer. (optional) if template does not expect any arguments. | -| `update` | boolean | ✅ | (optional) whether to update deployed contract. Default: `false` | +| `addressMap` | [AddressMap](#addressmap) | ✅ | (optional) object to use for address mapping of existing deployed contracts | +| `args` | [Any] | ✅ | (optional) arguments, which will be passed to contract initializer. (optional) if template does not expect any arguments. | +| `update` | boolean | ✅ | (optional) whether to update deployed contract. Default: `false` | #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Result of the deploying transaction. | #### Usage @@ -75,10 +74,9 @@ import { init, emulator, deployContractByName } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, {port}); - await emulator.start(port); + await init(basePath); + await emulator.start(); // We will deploy our contract to the address that corresponds to "Alice" alias const to = await getAccountAddress("Alice"); @@ -111,19 +109,19 @@ Deploys contract code specified as string. Returns the transaction result. Props object accepts the following fields: -| Name | Type | Optional | Description | -| -------------- | --------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `contractCode` | string | | string representation of contract | -| `name` | string | | name of the contract to be deployed. Should be the same as the name of the contract provided in `contractCode` | +| Name | Type | Optional | Description | +| -------------- | ------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| `contractCode` | string | | string representation of contract | +| `name` | string | | name of the contract to be deployed. Should be the same as the name of the contract provided in `contractCode` | | `to` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | ✅ | account address, where contract will be deployed. If this is not specified, framework will create new account with randomized alias. | -| `addressMap` | [AddressMap](#addressmap) | ✅ | object to use for import resolver. Default: `{}` | -| `args` | [Any] | ✅ | arguments, which will be passed to contract initializer. Default: `[]` | -| `update` | boolean | ✅ | whether to update deployed contract. Default: `false` | +| `addressMap` | [AddressMap](#addressmap) | ✅ | object to use for import resolver. Default: `{}` | +| `args` | [Any] | ✅ | arguments, which will be passed to contract initializer. Default: `[]` | +| `update` | boolean | ✅ | whether to update deployed contract. Default: `false` | #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Result of the deploying transaction. | #### Usage @@ -134,10 +132,9 @@ import { init, emulator, getAccountAddress, deployContract, executeScript } from (async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // We can specify, which account will hold the contract const to = await getAccountAddress("Alice"); @@ -155,7 +152,7 @@ import { init, emulator, getAccountAddress, deployContract, executeScript } from await deployContract({ to, name, code, args }); - const [balance,err] = await executeScript({ + const [balance, err] = await executeScript({ code: ` import Wallet from 0x01 pub fun main(): UInt{ @@ -184,8 +181,8 @@ Returns address of the account where the contract is currently deployed. #### Returns -| Type | Description | -| --------------------------------------------------- | --------------------- | +| Type | Description | +| ------------------------------------------------------------- | --------------------- | | [Address](https://docs.onflow.org/fcl/reference/api/#address) | `0x` prefixed address | #### Usage @@ -196,10 +193,9 @@ import { init, emulator, deployContractByName, getContractAddress } from "../src (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // if we omit "to" it will be deployed to Service Account // but let's pretend we don't know where it will be deployed :) @@ -210,7 +206,6 @@ import { init, emulator, deployContractByName, getContractAddress } from "../src await emulator.stop(); })(); - ``` 📣 Framework does not support contracts with identical names deployed to different accounts. While you can deploy contract @@ -221,16 +216,25 @@ to a new address, the internal system, which tracks where contracts are deployed Flow Javascript Testing Framework exposes `emulator` singleton allowing you to run and stop emulator instance programmatically. There are two methods available on it. -### `emulator.start(port, logging)` +### `emulator.start(options)` Starts emulator on a specified port. Returns Promise. #### Arguments -| Name | Type | Optional | Description | -| --------- | ------- | -------- | ----------------------------------------------------------------- | -| `port` | number | ✅ | number representing a port to use for access API. Default: `8080` | -| `logging` | boolean | ✅ | whether log messages from emulator shall be added to the output | +| Name | Type | Optional | Description | +| --------- | --------------- | -------- | ------------------------------------------------------ | +| `options` | EmulatorOptions | ✅ | an object containing options for starting the emulator | + +#### EmulatorOptions + +| Key | Type | Optional | Description | +| ----------- | ------- | -------- | --------------------------------------------------------------------------------- | +| `logging` | boolean | ✅ | whether log messages from emulator shall be added to the output (default: false) | +| `flags` | string | ✅ | custom command-line flags to supply to the emulator (default: "") | +| `adminPort` | number | ✅ | override the port which the emulator will run the admin server on (default: auto) | +| `restPort` | number | ✅ | override the port which the emulator will run the REST server on (default: auto) | +| `grpcPort` | number | ✅ | override the port which the emulator will run the GRPC server on (default: auto) | #### Returns @@ -246,19 +250,17 @@ import { emulator, init } from "../src"; (async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); + await init(basePath); // Start emulator instance on port 8080 - await emulator.start(port); + await emulator.start(); console.log("emulator is working"); // Stop running emulator await emulator.stop(); console.log("emulator has been stopped"); })(); - ``` ### `emulator.stop()` @@ -284,10 +286,9 @@ describe("test setup", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); }); // Stop emulator, so it could be restarted @@ -321,10 +322,9 @@ describe("test setup", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); }); // Stop emulator, so it could be restarted @@ -358,8 +358,8 @@ Fetch current FlowToken balance of account specified by address #### Arguments -| Name | Type | Description | -| --------- | --------------------------------------------------- | ------------------------------- | +| Name | Type | Description | +| --------- | ------------------------------------------------------------- | ------------------------------- | | `address` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | address of the account to check | #### Returns @@ -375,15 +375,14 @@ import { init, emulator, getAccountAddress, getFlowBalance } from "flow-js-testi const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const Alice = await getAccountAddress("Alice"); const [result, error] = await getFlowBalance(Alice); - console.log( { result }, { error }); + console.log({ result }, { error }); await emulator.stop(); }; @@ -399,15 +398,15 @@ Sends transaction to mint specified amount of FLOW token and send it to recipien #### Arguments -| Name | Type | Description | -| ----------- | --------------------------------------------------- | ---------------------------------------------------------- | +| Name | Type | Description | +| ----------- | ------------------------------------------------------------- | ---------------------------------------------------------- | | `recipient` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | address of the account to check | -| `amount` | string | UFix64 amount of FLOW tokens to mint and send to recipient | +| `amount` | string | UFix64 amount of FLOW tokens to mint and send to recipient | #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Transaction result | #### Usage @@ -418,17 +417,16 @@ import { init, emulator, getAccountAddress, getFlowBalance, mintFlow } from "../ (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Get address for account with alias "Alice" const Alice = await getAccountAddress("Alice"); // Get initial balance const [initialBalance] = await getFlowBalance(Alice); - console.log( initialBalance ); + console.log(initialBalance); // Add 1.0 FLOW tokens to Alice account await mintFlow(Alice, "1.0"); @@ -439,7 +437,6 @@ import { init, emulator, getAccountAddress, getFlowBalance, mintFlow } from "../ await emulator.stop(); })(); - ``` ## Init @@ -449,9 +446,7 @@ You can do it with provided `init` method. ### init( basePath, options) -Initializes framework variables and specifies port to use for HTTP and grpc access. -`port` is set to 8080 by default. grpc port is calculated to `3569 + (port - 8080)` to allow multiple instances -of emulator to be run in parallel. +Initializes framework variables. #### Arguments @@ -464,7 +459,6 @@ of emulator to be run in parallel. | Name | Type | Optional | Description | | ------ | ---- | -------- | ------------------------------- | -| `port` | | ✅ | http port for access node | | `pkey` | | ✅ | private key for service account | #### Returns @@ -510,10 +504,9 @@ import { init, emulator, getBlockOffset } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - init(basePath, port); - await emulator.start(port); + init(basePath); + await emulator.start(); const [blockOffset, err] = await getBlockOffset(); console.log({ blockOffset }, { err }); @@ -557,10 +550,9 @@ import { const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - init(basePath, port); - await emulator.start(port); + init(basePath); + await emulator.start(); // Offset current block height by 42 await setBlockOffset(42); @@ -604,8 +596,8 @@ Ensure transaction does not throw and sealed. #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Transaction result | #### Usage @@ -627,9 +619,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -675,8 +666,8 @@ Ensure interaction throws an error. You might want to use this to test incorrect #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Transaction result | #### Usage @@ -698,9 +689,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -763,9 +753,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -809,11 +798,11 @@ Provides explicit control over how you pass values. `props` object accepts following fields: -| Name | Type | Optional | Description | -| -------------- | ----------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------ | -| `code` | string | ✅ | string representation of Cadence script | -| `name` | string | ✅ | name of the file in `scripts` folder to use (sans `.cdc` extension) | -| `args` | array | ✅ | an array of arguments to pass to script. Optional if script does not expect any arguments. | +| Name | Type | Optional | Description | +| -------------- | ------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------ | +| `code` | string | ✅ | string representation of Cadence script | +| `name` | string | ✅ | name of the file in `scripts` folder to use (sans `.cdc` extension) | +| `args` | array | ✅ | an array of arguments to pass to script. Optional if script does not expect any arguments. | | `transformers` | array[[CadenceTransformer](#cadencetransformer)] | ✅ | an array of operators to modify the code, before submitting it to network | > ⚠️ **Required:** Either `code` or `name` field shall be specified. Method will throw an error if both of them are empty. @@ -821,8 +810,8 @@ Provides explicit control over how you pass values. #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------- | +| Type | Description | +| --------------------------------------------------------------------------- | ------------- | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Script result | #### Usage @@ -833,12 +822,11 @@ import { init, emulator, executeScript } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - init(basePath, { port }); + init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define code and arguments we want to pass const code = ` @@ -874,8 +862,8 @@ Cadence files. #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------- | +| Type | Description | +| --------------------------------------------------------------------------- | ------------- | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Script result | #### Usage @@ -886,12 +874,11 @@ import { init, emulator, executeScript } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - init(basePath, port); + init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define arguments we want to pass const args = ["Hello, from Cadence"]; @@ -922,14 +909,14 @@ Provides explicit control over how you pass values. `props` object accepts following fields: -| Name | Type | Optional | Description | -| -------------- | ----------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------- | -| `code` | string | ✅ | string representation of Cadence transaction | -| `name` | string | ✅ | name of the file in `transaction` folder to use (sans `.cdc` extension) | -| `args` | [Any] | ✅ | an array of arguments to pass to transaction. Optional if transaction does not expect any arguments. | -| `signers` | [Address] | ✅ | an array of [Address](https://docs.onflow.org/fcl/reference/api/#address) representing transaction autorizers | -| `addressMap` | [AddressMap](#addressmap) | ✅ | name/address map to use as lookup table for addresses in import statements | -| `transformers` | array[[CadenceTransformer](#cadencetransformer)] | ✅ | an array of operators to modify the code, before submitting it to network | +| Name | Type | Optional | Description | +| -------------- | ------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------- | +| `code` | string | ✅ | string representation of Cadence transaction | +| `name` | string | ✅ | name of the file in `transaction` folder to use (sans `.cdc` extension) | +| `args` | [Any] | ✅ | an array of arguments to pass to transaction. Optional if transaction does not expect any arguments. | +| `signers` | [Address] | ✅ | an array of [Address](https://docs.onflow.org/fcl/reference/api/#address) representing transaction autorizers | +| `addressMap` | [AddressMap](#addressmap) | ✅ | name/address map to use as lookup table for addresses in import statements | +| `transformers` | array[[CadenceTransformer](#cadencetransformer)] | ✅ | an array of operators to modify the code, before submitting it to network | > ⚠️ **Required:** Either `code` or `name` field shall be specified. Method will throw an error if both of them are empty. > If `name` field provided, framework will source code from file and override value passed via `code` field. @@ -941,8 +928,8 @@ Provides explicit control over how you pass values. #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Interaction result | #### Usage @@ -953,12 +940,11 @@ import { init, emulator, sendTransaction, getAccountAddress } from "flow-js-test const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - await init(basePath, { port }); + await init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define code and arguments we want to pass const code = ` @@ -987,16 +973,16 @@ main(); This signature provides simplified way to send a transaction, since most of the time you will utilize existing Cadence files. -| Name | Type | Optional | Description | -| --------- | ------ | -------- | ---------------------------------------------------------------------------------------------------- | -| `name` | string | ✅ | name of the file in `transaction` folder to use (sans `.cdc` extension) | -| `signers` | array | ✅ | an array of [Address](https://docs.onflow.org/fcl/reference/api/#address) representing transaction autorizers | -| `args` | [Any] | ✅ | an array of arguments to pass to transaction. Optional if transaction does not expect any arguments. | +| Name | Type | Optional | Description | +| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------- | +| `name` | string | ✅ | name of the file in `transaction` folder to use (sans `.cdc` extension) | +| `signers` | array | ✅ | an array of [Address](https://docs.onflow.org/fcl/reference/api/#address) representing transaction autorizers | +| `args` | [Any] | ✅ | an array of arguments to pass to transaction. Optional if transaction does not expect any arguments. | #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Interaction result | #### Usage @@ -1007,12 +993,11 @@ import { init, emulator, sendTransaction } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - await init(basePath, { port }); + await init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define arguments we want to pass const args = ["Hello, Cadence"]; @@ -1092,10 +1077,9 @@ import { init, emulator, getContractCode } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Let's assume we need to import MessageContract await deployContractByName({ name: "MessageContract" }); @@ -1138,10 +1122,9 @@ import { init, emulator, getTransactionCode } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Let's assume we need to import MessageContract await deployContractByName({ name: "MessageContract" }); @@ -1185,10 +1168,9 @@ import { init, emulator, getScriptCode } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Let's assume we need to import MessageContract await deployContractByName({ name: "MessageContract" }); diff --git a/docs/contracts.md b/docs/contracts.md index f840f4ed..e3b9bc5c 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -14,18 +14,18 @@ Deploys contract code located inside a Cadence file. Returns the transaction res Props object accepts the following fields: -| Name | Type | Optional | Description | -| ------------ | --------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | | name of the file in `contracts` folder (sans `.cdc` extension) and name of the contract (please note those should be the same) | +| Name | Type | Optional | Description | +| ------------ | ------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `name` | string | | name of the file in `contracts` folder (sans `.cdc` extension) and name of the contract (please note those should be the same) | | `to` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | ✅ | (optional) account address, where contract will be deployed. If this is not specified, framework will create new account with randomized alias. | -| `addressMap` | [AddressMap](api.md#addressmap) | ✅ | (optional) object to use for address mapping of existing deployed contracts | -| `args` | [Any] | ✅ | (optional) arguments, which will be passed to contract initializer. (optional) if template does not expect any arguments. | -| `update` | boolean | ✅ | (optional) whether to update deployed contract. Default: `false` | +| `addressMap` | [AddressMap](api.md#addressmap) | ✅ | (optional) object to use for address mapping of existing deployed contracts | +| `args` | [Any] | ✅ | (optional) arguments, which will be passed to contract initializer. (optional) if template does not expect any arguments. | +| `update` | boolean | ✅ | (optional) whether to update deployed contract. Default: `false` | #### Returns -| Type | Description | -| ----------------------------------------------------------------- | ------------------------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Result of the deploying transaction. | Usage: @@ -36,10 +36,9 @@ import { init, emulator, deployContractByName } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - init(basePath, port); - await emulator.start(port); + init(basePath); + await emulator.start(); // We will deploy our contract to the address that corresponds to "Alice" alias const to = await getAccountAddress("Alice"); @@ -71,14 +70,14 @@ Deploys contract code specified as string. Returns transaction result. Props object accepts the following fields: -| Name | Type | Optional | Description | -| -------------- | --------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | -| `contractCode` | string | | string representation of contract | -| `name` | string | | name of the contract to be deployed. Should be the same as the name of the contract provided in `contractCode` | +| Name | Type | Optional | Description | +| -------------- | ------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| `contractCode` | string | | string representation of contract | +| `name` | string | | name of the contract to be deployed. Should be the same as the name of the contract provided in `contractCode` | | `to` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | ✅ | account address, where contract will be deployed. If this is not specified, framework will create new account with randomized alias. | -| `addressMap` | [AddressMap](api.md#addressmap) | ✅ | object to use for import resolver. Default: `{}` | -| `args` | [Any] | ✅ | arguments, which will be passed to contract initializer. Default: `[]` | -| `update` | boolean | ✅ | whether to update deployed contract. Default: `false` | +| `addressMap` | [AddressMap](api.md#addressmap) | ✅ | object to use for import resolver. Default: `{}` | +| `args` | [Any] | ✅ | arguments, which will be passed to contract initializer. Default: `[]` | +| `update` | boolean | ✅ | whether to update deployed contract. Default: `false` | Usage: @@ -88,10 +87,9 @@ import { init, emulator, deployContract } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port, false); + await init(basePath); + await emulator.start(); const to = await getAccountAddress("Alice"); const name = "Wallet"; @@ -112,7 +110,7 @@ const main = async () => { args, }); - console.log( deploymentResult, error ); + console.log(deploymentResult, error); await emulator.stop(); }; @@ -137,10 +135,9 @@ import { getContractAddress } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port, false); + await init(basePath); + await emulator.start(); // if we ommit "to" it will be deployed to a newly generated address with "unknown" alias await deployContractByName({ name: "HelloWorld" }); diff --git a/docs/emulator.md b/docs/emulator.md index f443b690..5924904c 100644 --- a/docs/emulator.md +++ b/docs/emulator.md @@ -4,18 +4,34 @@ sidebar_title: Emulator description: How to start a new instance of emulator --- -Flow Javascript Testing Framework exposes `emulator` singleton allowing you to start and stop an emulator instance programmatically. There are two methods available on it. +Flow Javascript Testing Framework exposes `emulator` singleton allowing you to run and stop emulator instance +programmatically. There are two methods available on it. -## `emulator.start(port, logging)` +## `emulator.start(options)` -Starts emulator on a specified port. Returns Promise. +Starts emulator on random available port, unless overriden in options. Returns Promise. #### Arguments -| Name | Type | Optional | Description | -| --------- | ------- | -------- | ----------------------------------------------------------------- | -| `port` | number | ✅ | number representing a port to use for access API. Default: `8080` | -| `logging` | boolean | ✅ | whether log messages from emulator shall be added to the output | +| Name | Type | Optional | Description | +| --------- | --------------- | -------- | ------------------------------------------------------ | +| `options` | EmulatorOptions | ✅ | an object containing options for starting the emulator | + +#### EmulatorOptions + +| Key | Type | Optional | Description | +| ----------- | ------- | -------- | --------------------------------------------------------------------------------- | +| `logging` | boolean | ✅ | whether log messages from emulator shall be added to the output (default: false) | +| `flags` | string | ✅ | custom command-line flags to supply to the emulator (default: "") | +| `adminPort` | number | ✅ | override the port which the emulator will run the admin server on (default: auto) | +| `restPort` | number | ✅ | override the port which the emulator will run the REST server on (default: auto) | +| `grpcPort` | number | ✅ | override the port which the emulator will run the GRPC server on (default: auto) | + +#### Returns + +| Type | Description | +| ------------------- | ---------------------------------------------------------------- | +| [Promise](#Promise) | Promise, which resolves to true if emulator started successfully | #### Usage @@ -26,12 +42,11 @@ describe("test setup", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); + await init(basePath); // Start emulator instance on port 8080 - await emulator.start(port); + await emulator.start(); }); }); ``` @@ -53,10 +68,9 @@ describe("test setup", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); }); // Stop emulator, so it could be restarted @@ -85,10 +99,9 @@ describe("test setup", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); }); // Stop emulator, so it could be restarted @@ -100,7 +113,7 @@ describe("test setup", () => { // Turn on logging from begining emulator.setLogging(true); // some asserts and interactions - + // Turn off logging for later calls emulator.setLogging(false); // more asserts and interactions here diff --git a/docs/examples/metadata.md b/docs/examples/metadata.md index e0126214..88dd8bf0 100644 --- a/docs/examples/metadata.md +++ b/docs/examples/metadata.md @@ -14,12 +14,11 @@ import { executeScript } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - await init(basePath, { port }); + await init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); const code = ` pub fun main(metadata: {String: String}): String{ @@ -33,7 +32,7 @@ const main = async () => { // If something goes wrong with script execution, the method will throw an error // so we need to catch it and proce const [name, err] = await shallResolve(executeScript({ code, args })); - console.log( name, err ); + console.log(name, err); await emulator.stop(); }; @@ -65,4 +64,4 @@ const args = [ ``` Framework will try to resolve the types to the best of its abilities. If you encounter an error for your use case, -please create an issue here: [https://github.com/onflow/flow-js-testing/issues](https://github.com/onflow/flow-js-testing/issues) \ No newline at end of file +please create an issue here: [https://github.com/onflow/flow-js-testing/issues](https://github.com/onflow/flow-js-testing/issues) diff --git a/docs/execute-scripts.md b/docs/execute-scripts.md index 2392a1df..83b666a9 100644 --- a/docs/execute-scripts.md +++ b/docs/execute-scripts.md @@ -38,9 +38,9 @@ const main = async () => { const port = 8080; // Init framework - init(basePath, { port }); + init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define code and arguments we want to pass const code = ` @@ -52,7 +52,7 @@ const main = async () => { `; const args = ["Hello, from Cadence"]; - const [result,e] = await executeScript({ code, args }); + const [result, e] = await executeScript({ code, args }); console.log(result, e); // Stop emulator instance @@ -82,19 +82,18 @@ import { init, emulator, executeScript } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - init(basePath, port); + init(basePath); // Start emulator - await emulator.start(port, false); + await emulator.start(); // Define arguments we want to pass const args = ["Hello, from Cadence"]; - const [result,error] = await executeScript("log-message", args); + const [result, error] = await executeScript("log-message", args); console.log(result, error); - + await emulator.stop(); }; diff --git a/docs/flow-token.md b/docs/flow-token.md index 443da0a6..6d00fe75 100644 --- a/docs/flow-token.md +++ b/docs/flow-token.md @@ -13,8 +13,8 @@ Returns current FLOW token balance of the specified account. #### Arguments -| Name | Type | Description | -| --------- | --------------------------------------------------- | ------------------------------- | +| Name | Type | Description | +| --------- | ------------------------------------------------------------- | ------------------------------- | | `address` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | address of the account to check | #### Returns @@ -30,10 +30,9 @@ import { init, emulator, getAccountAddress, getFlowBalance } from "flow-js-testi const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const Alice = await getAccountAddress("Alice"); @@ -54,10 +53,10 @@ Sends transaction to mint the specified amount of FLOW and send it to recipient. #### Arguments -| Name | Type | Description | -| ----------- | --------------------------------------------------- | ---------------------------------------------------------- | +| Name | Type | Description | +| ----------- | ------------------------------------------------------------- | ---------------------------------------------------------- | | `recipient` | [Address](https://docs.onflow.org/fcl/reference/api/#address) | address of the account to check | -| `amount` | string | UFix64 amount of FLOW tokens to mint and send to recipient | +| `amount` | string | UFix64 amount of FLOW tokens to mint and send to recipient | #### Usage @@ -66,15 +65,14 @@ import { init, emulator, mintFlow } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const Alice = await getAccountAddress("Alice"); const amount = "42.0"; const [mintResult, error] = await mintFlow(Alice); - console.log( mintResult, error ); + console.log(mintResult, error); await emulator.stop(); }; diff --git a/docs/init.md b/docs/init.md index 7e1c0330..faaaefc3 100644 --- a/docs/init.md +++ b/docs/init.md @@ -9,9 +9,7 @@ You can do it with provided `init` method. ### init( basePath, options) -Initializes framework variables and specifies port to use for HTTP and grpc access. -`port` is set to 8080 by default. gRPC port is calculated to `3569 + (port - 8080)` to allow multiple instances -of emulator to be run in parallel. +Initializes framework variables. #### Arguments @@ -24,7 +22,6 @@ of emulator to be run in parallel. | Name | Type | Optional | Description | | ------ | ---- | -------- | ------------------------------- | -| `port` | | ✅ | http port for access node | | `pkey` | | ✅ | private key for service account | #### Usage diff --git a/docs/jest-helpers.md b/docs/jest-helpers.md index 41fff00c..4668712d 100644 --- a/docs/jest-helpers.md +++ b/docs/jest-helpers.md @@ -13,27 +13,21 @@ Ensure transaction did not throw and was sealed. #### Arguments -| Name | Type | Description | -| ---- | --------------------------- | ---------------------------------------------------- | -| `ix` | [Interaction](api.md#interaction) | interaction, either in form of a Promise or function | +| Name | Type | Description | +| ---- | --------------------------------- | ---------------------------------------------------- | +| `ix` | [Interaction](api.md#interaction) | interaction, either in form of a Promise or function | #### Returns -| Type | Description | -| --------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Transaction result | #### Usage ```javascript import path from "path"; -import { - init, - emulator, - shallPass, - sendTransaction, - getAccountAddress, -} from "flow-js-testing"; +import { init, emulator, shallPass, sendTransaction, getAccountAddress } from "flow-js-testing"; // We need to set timeout for a higher number, because some transactions might take up some time jest.setTimeout(10000); @@ -42,9 +36,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -85,14 +78,14 @@ Returns Promise, which contains result, when resolved. #### Arguments -| Name | Type | Description | -| ---- | --------------------------- | ---------------------------------------------------- | +| Name | Type | Description | +| ---- | --------------------------------- | ---------------------------------------------------- | | `ix` | [Interaction](api.md#interaction) | transaction, either in form of a Promise or function | #### Returns -| Type | Description | -| --------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Transaction result | #### Usage @@ -114,9 +107,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted @@ -156,14 +148,14 @@ Ensure interaction resolves without throwing errors. #### Arguments -| Name | Type | Description | -| ---- | --------------------------- | ---------------------------------------------------- | +| Name | Type | Description | +| ---- | --------------------------------- | ---------------------------------------------------- | | `ix` | [Interaction](api.md#interaction) | interaction, either in form of a Promise or function | #### Returns -| Type | Description | -| --------------------------------------- | ------------------ | +| Type | Description | +| --------------------------------------------------------------------------- | ------------------ | | [ResponseObject](https://docs.onflow.org/fcl/reference/api/#responseobject) | Transaction result | #### Usage @@ -179,9 +171,8 @@ describe("interactions - sendTransaction", () => { // Instantiate emulator and path to Cadence files beforeEach(async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - return emulator.start(port); + await init(basePath); + return emulator.start(); }); // Stop emulator, so it could be restarted diff --git a/docs/send-transactions.md b/docs/send-transactions.md index fda4ee4f..ef93398f 100644 --- a/docs/send-transactions.md +++ b/docs/send-transactions.md @@ -17,12 +17,12 @@ Provides explicit control over how you pass values. `props` object accepts following fields: -| Name | Type | Optional | Description | -| ------------ | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------- | -| `code` | string | ✅ | string representation of Cadence transaction | -| `name` | string | ✅ | name of the file in `transaction` folder to use (sans `.cdc` extension) | -| `args` | [Any] | ✅ | an array of arguments to pass to transaction. Optional if transaction does not expect any arguments. | -| `signers` | [Address] | ✅ | an array of [Address](#Address) representing transaction autorizers | +| Name | Type | Optional | Description | +| ------------ | ------------------------------- | -------- | ---------------------------------------------------------------------------------------------------- | +| `code` | string | ✅ | string representation of Cadence transaction | +| `name` | string | ✅ | name of the file in `transaction` folder to use (sans `.cdc` extension) | +| `args` | [Any] | ✅ | an array of arguments to pass to transaction. Optional if transaction does not expect any arguments. | +| `signers` | [Address] | ✅ | an array of [Address](#Address) representing transaction autorizers | | `addressMap` | [AddressMap](api.md#addressmap) | ✅ | name/address map to use as lookup table for addresses in import statements | > ⚠️ **Required:** Either `code` or `name` field shall be specified. Method will throw an error if both of them are empty. @@ -41,12 +41,11 @@ import { init, emulator, sendTransaction, getAccountAddress } from "flow-js-test const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - await init(basePath, { port }); + await init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define code and arguments we want to pass const code = ` @@ -62,7 +61,7 @@ const main = async () => { const [tx, error] = await sendTransaction({ code, args, signers }); console.log(tx, error); - + // Stop emulator instance await emulator.stop(); }; @@ -89,12 +88,11 @@ import { init, emulator, sendTransaction } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; // Init framework - await init(basePath, { port }); + await init(basePath); // Start emulator - await emulator.start(port); + await emulator.start(); // Define arguments we want to pass const args = ["Hello, Cadence"]; diff --git a/docs/templates.md b/docs/templates.md index 79dc2eca..449649c9 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -13,11 +13,11 @@ them here as well. Returns Cadence template as string with addresses replaced using addressMap -| Name | Type | Optional | Description | -| ------------ | ------------------------------ | -------- | --------------------------------------------------------------------------------------------------------- | -| `file` | string | | relative (to the place from where the script was called) or absolute path to the file containing the code | +| Name | Type | Optional | Description | +| ------------ | --------------------------------- | -------- | --------------------------------------------------------------------------------------------------------- | +| `file` | string | | relative (to the place from where the script was called) or absolute path to the file containing the code | | `addressMap` | [AddressMap](./api.md#addressmap) | ✅ | object to use for address mapping of existing deployed contracts. Default: `{}` | -| `byAddress` | boolean | ✅ | whether addressMap is `{name:address}` or `{address:address}` type. Default: `false` | +| `byAddress` | boolean | ✅ | whether addressMap is `{name:address}` or `{address:address}` type. Default: `false` | #### Returns @@ -48,9 +48,9 @@ Returns Cadence template from file with `name` in `_basepath_/contracts` folder #### Arguments -| Name | Type | Optional | Description | -| ------------ | ------------------------------ | -------- | ---------------------------------------------------------------- | -| `name` | string | | name of the contract template | +| Name | Type | Optional | Description | +| ------------ | --------------------------------- | -------- | ---------------------------------------------------------------- | +| `name` | string | | name of the contract template | | `addressMap` | [AddressMap](./api.md#addressmap) | ✅ | object to use for address mapping of existing deployed contracts | #### Returns @@ -67,10 +67,9 @@ import { init, emulator, getContractCode } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Let's assume we need to import MessageContract await deployContractByName({ name: "MessageContract" }); @@ -94,9 +93,9 @@ Returns Cadence template from file with `name` in `_basepath_/transactions` fold #### Arguments -| Name | Type | Optional | Description | -| ------------ | ------------------------------ | -------- | ---------------------------------------------------------------- | -| `name` | string | | name of the transaction template | +| Name | Type | Optional | Description | +| ------------ | --------------------------------- | -------- | ---------------------------------------------------------------- | +| `name` | string | | name of the transaction template | | `addressMap` | [AddressMap](./api.md#addressmap) | ✅ | object to use for address mapping of existing deployed contracts | #### Returns @@ -113,10 +112,9 @@ import { init, emulator, getTransactionCode } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Let's assume we need to import MessageContract await deployContractByName({ name: "MessageContract" }); @@ -141,9 +139,9 @@ Returns Cadence template from file with `name` in `_basepath_/scripts` folder #### Arguments -| Name | Type | Optional | Description | -| ------------ | ------------------------------ | -------- | ---------------------------------------------------------------- | -| `name` | string | | name of the script template | +| Name | Type | Optional | Description | +| ------------ | --------------------------------- | -------- | ---------------------------------------------------------------- | +| `name` | string | | name of the script template | | `addressMap` | [AddressMap](./api.md#addressmap) | ✅ | object to use for address mapping of existing deployed contracts | #### Returns @@ -160,10 +158,9 @@ import { init, emulator, getScriptCode } from "flow-js-testing"; const main = async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Let's assume we need to import MessageContract await deployContractByName({ name: "MessageContract" }); diff --git a/examples/01-get-account-address.js b/examples/01-get-account-address.js index 31b7777c..88d8cfe1 100644 --- a/examples/01-get-account-address.js +++ b/examples/01-get-account-address.js @@ -1,15 +1,14 @@ -import path from "path" +import path from "path"; import { init, emulator, getAccountAddress } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const Alice = await getAccountAddress("Alice"); console.log({ Alice }); await emulator.stop(); -})(); \ No newline at end of file +})(); diff --git a/examples/02-deploy-contract-by-name.js b/examples/02-deploy-contract-by-name.js index f8fb4273..16fa95ba 100644 --- a/examples/02-deploy-contract-by-name.js +++ b/examples/02-deploy-contract-by-name.js @@ -4,11 +4,10 @@ import { init, emulator, deployContractByName, executeScript } from "../src"; (async () => { // Init framework const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); + await init(basePath); // Start Emulator - await emulator.start(port); + await emulator.start(); // Deploy contract Greeting with single argument await deployContractByName({ diff --git a/examples/03-deploy-contract.js b/examples/03-deploy-contract.js index 143bccc7..79e36252 100644 --- a/examples/03-deploy-contract.js +++ b/examples/03-deploy-contract.js @@ -3,10 +3,9 @@ import { init, emulator, getAccountAddress, deployContract, executeScript } from (async () => { const basePath = path.resolve(__dirname, "../cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // We can specify, which account will hold the contract const to = await getAccountAddress("Alice"); diff --git a/examples/04-get-contract-address.js b/examples/04-get-contract-address.js index 10c820fe..fab9e476 100644 --- a/examples/04-get-contract-address.js +++ b/examples/04-get-contract-address.js @@ -3,10 +3,9 @@ import { init, emulator, deployContractByName, getContractAddress } from "../src (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // if we omit "to" it will be deployed to Service Account // but let's pretend we don't know where it will be deployed :) diff --git a/examples/05-emulator-management.js b/examples/05-emulator-management.js index 8ff35688..81c525af 100644 --- a/examples/05-emulator-management.js +++ b/examples/05-emulator-management.js @@ -3,7 +3,6 @@ import { emulator, init, executeScript } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; await init(basePath); // Let's define simple method to log message to emulator console @@ -25,8 +24,8 @@ import { emulator, init, executeScript } from "../src"; // emulator.addFilter("service"); emulator.addFilter("info"); - // Start emulator instance on port 8080 - await emulator.start(port, { logging }); + // Start emulator instance on available ports + await emulator.start({ logging }); // This line will be visible in emulator output emulator.setLogging(true); diff --git a/examples/06-flow-management.js b/examples/06-flow-management.js index bde7940f..0b51602f 100644 --- a/examples/06-flow-management.js +++ b/examples/06-flow-management.js @@ -3,10 +3,9 @@ import { init, emulator, getAccountAddress, getFlowBalance, mintFlow } from "../ (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // Get address for account with alias "Alice" const Alice = await getAccountAddress("Alice"); diff --git a/examples/07-block-offset.js b/examples/07-block-offset.js index 9a8765cb..bdff5a2d 100644 --- a/examples/07-block-offset.js +++ b/examples/07-block-offset.js @@ -10,10 +10,9 @@ import { (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const [initialBlockOffset] = await getBlockOffset(); console.log({ initialBlockOffset }); diff --git a/examples/08-execute-script.js b/examples/08-execute-script.js index b27c4b8b..39078bba 100644 --- a/examples/08-execute-script.js +++ b/examples/08-execute-script.js @@ -3,10 +3,9 @@ import { init, emulator, executeScript } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); // We have created a file called "log-args.cdc" under "./cadence/scripts" folder. // It's available for use since we configured framework to use "./cadence" folder as root @@ -45,7 +44,7 @@ import { init, emulator, executeScript } from "../src"; // "executeScript" also supports short form, accepting name of the file in "scripts folder // and array of arguments - const [shortForm] = await executeScript("hello") + const [shortForm] = await executeScript("hello"); console.log({ shortForm }); await emulator.stop(); diff --git a/examples/09-send-transaction.js b/examples/09-send-transaction.js index fdd82d4d..1145bea3 100644 --- a/examples/09-send-transaction.js +++ b/examples/09-send-transaction.js @@ -3,10 +3,9 @@ import { init, emulator, getAccountAddress, sendTransaction } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port, true); + await init(basePath); + await emulator.start(); emulator.addFilter(`debug`); diff --git a/examples/10-templates.js b/examples/10-templates.js index dd603031..491d0680 100644 --- a/examples/10-templates.js +++ b/examples/10-templates.js @@ -10,10 +10,9 @@ import { (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const addressMap = { Profile: "0xf8d6e0586b0a20c7", diff --git a/examples/100-pass-array-of-dictionaries.js b/examples/100-pass-array-of-dictionaries.js index dace09e2..39ac5372 100644 --- a/examples/100-pass-array-of-dictionaries.js +++ b/examples/100-pass-array-of-dictionaries.js @@ -1,16 +1,11 @@ import path from "path"; -import { - init, - emulator, - executeScript, -} from "../src"; +import { init, emulator, executeScript } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const code = ` pub fun main(meta: [{String:String}], key: String): String?{ @@ -24,7 +19,7 @@ import { nickname: "Giorgio", }, ], - "name" + "name", ]; const result = await executeScript({ code, args }); diff --git a/examples/101-pass-int-dictionary.js b/examples/101-pass-int-dictionary.js index c5f9bea7..4fda5210 100644 --- a/examples/101-pass-int-dictionary.js +++ b/examples/101-pass-int-dictionary.js @@ -3,10 +3,9 @@ import { init, emulator, executeScript } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const code = ` pub fun main(data: {UInt32: UInt32}, key: UInt32): UInt32?{ diff --git a/examples/102-pass-string-to-int-dictionary.js b/examples/102-pass-string-to-int-dictionary.js index 680e29b3..92edaf7d 100644 --- a/examples/102-pass-string-to-int-dictionary.js +++ b/examples/102-pass-string-to-int-dictionary.js @@ -3,10 +3,9 @@ import { init, emulator, executeScript } from "../src"; (async () => { const basePath = path.resolve(__dirname, "./cadence"); - const port = 8080; - await init(basePath, { port }); - await emulator.start(port); + await init(basePath); + await emulator.start(); const code = ` pub fun main(data: {String: UInt32}, key: String): UInt32?{ @@ -14,7 +13,7 @@ import { init, emulator, executeScript } from "../src"; } `; - const args = [{ "cadence": 0, "test": 1337 }, "cadence"]; + const args = [{ cadence: 0, test: 1337 }, "cadence"]; const [result] = await executeScript({ code, args }); console.log({ result }); diff --git a/flow.json b/flow.json index 6cb72570..16248416 100644 --- a/flow.json +++ b/flow.json @@ -1,21 +1,21 @@ { - "emulators": { - "default": { - "port": 3569, - "serviceAccount": "emulator-account" - } - }, - "contracts": {}, - "networks": { - "emulator": "127.0.0.1:3569", - "mainnet": "access.mainnet.nodes.onflow.org:9000", - "testnet": "access.devnet.nodes.onflow.org:9000" - }, - "accounts": { - "emulator-account": { - "address": "f8d6e0586b0a20c7", - "key": "f9cd76bf71c3371c76743ff22a0a1ee6657c63c46c62bd86a20266471fe0ea70" - } - }, - "deployments": {} -} \ No newline at end of file + "emulators": { + "default": { + "port": 3569, + "serviceAccount": "emulator-account" + } + }, + "contracts": {}, + "networks": { + "emulator": "127.0.0.1:3569", + "mainnet": "access.mainnet.nodes.onflow.org:9000", + "testnet": "access.devnet.nodes.onflow.org:9000" + }, + "accounts": { + "emulator-account": { + "address": "f8d6e0586b0a20c7", + "key": "f9cd76bf71c3371c76743ff22a0a1ee6657c63c46c62bd86a20266471fe0ea70" + } + }, + "deployments": {} +} diff --git a/src/cli/templates/test.js b/src/cli/templates/test.js index 2c5219a7..32107bb7 100644 --- a/src/cli/templates/test.js +++ b/src/cli/templates/test.js @@ -28,13 +28,10 @@ describe("${name}", ()=>{ ? "\n\t\t// You can specify different port to parallelize execution of describe blocks" : "" } - const port = 8080; ${ - comments ? "\n\t\t// Setting logging flag to true will pipe emulator output to console" : "" - } const logging = false; - await init(basePath, { port }); - return emulator.start(port, logging); + await init(basePath); + return emulator.start({ logging }); }); ${comments ? "\n // Stop emulator, so it could be restarted" : ""} afterEach(async () => { diff --git a/src/emulator.js b/src/emulator.js index 70e6ed4d..677f8476 100644 --- a/src/emulator.js +++ b/src/emulator.js @@ -17,6 +17,7 @@ */ import { send, build, getBlock, decode } from "@onflow/fcl"; import { config } from "@onflow/config"; +import { getAvailablePorts } from "./utils"; const { spawn } = require("child_process"); @@ -24,12 +25,12 @@ const DEFAULT_HTTP_PORT = 8080; const DEFAULT_GRPC_PORT = 3569; const print = { - "log": console.log, - "service": console.log, - "info": console.log, - "error": console.error, - "warn": console.warn -} + log: console.log, + service: console.log, + info: console.log, + error: console.error, + warn: console.warn, +}; /** Class representing emulator */ export class Emulator { @@ -62,12 +63,12 @@ export class Emulator { } } - checkLevel(message, level){ - if(level === "debug"){ + checkLevel(message, level) { + if (level === "debug") { // We might need to find a better way for this, but this will do for now... - return message.includes("LOG") ? "log" : level + return message.includes("LOG") ? "log" : level; } - return level + return level; } extractKeyValue(str) { @@ -104,21 +105,40 @@ export class Emulator { * @param {boolean} logging - whether logs shall be printed * @returns Promise<*> */ - async start(port = DEFAULT_HTTP_PORT, options = {}) { - // config access node - config().put("accessNode.api", `http://localhost:${port}`); + async start(options = {}) { + // populate emulator ports with available ports + [this.grpcPort, this.restPort, this.adminPort] = await getAvailablePorts(3); + + // override ports if specified in options + this.grpcPort = options.grpcPort || this.grpcPort; + this.restPort = options.restPort || this.restPort; + this.adminPort = options.adminPort || this.adminPort; + + // Support deprecated start call using static port + if (arguments.length > 1 || typeof arguments[0] === "number") { + console.warn(`Calling emulator.start with the port argument is now deprecated in favour of dynamically selected ports and will be removed in future versions of flow-js-testing. +Please refrain from supplying this argument, as using it may cause unintended consequences. +More info: https://github.com/onflow/flow-js-testing/blob/master/TRANSITIONS.md#001-deprecate-emulatorstart-port-argument`); + + [this.adminPort, options = {}] = arguments; + + const offset = this.adminPort - DEFAULT_HTTP_PORT; + this.grpcPort = DEFAULT_GRPC_PORT + offset; + } const { flags = "", logging = false } = options; - const offset = port - DEFAULT_HTTP_PORT; - let grpc = DEFAULT_GRPC_PORT + offset; + + // config access node + config().put("accessNode.api", `http://localhost:${this.adminPort}`); this.logging = logging; this.process = spawn("flow", [ "emulator", "--verbose", `--log-format=JSON`, - `--admin-port=${port}`, - `--port=${grpc}`, + `--rest-port=${this.restPort}`, + `--admin-port=${this.adminPort}`, + `--port=${this.grpcPort}`, flags, ]); this.logProcessor = (item) => item; @@ -146,7 +166,7 @@ export class Emulator { }); } for (let i = 0; i < filtered.length; i++) { - const item = data[i] + const item = data[i]; const { msg } = item; const level = this.checkLevel(msg, item.level); this.log(`${level.toUpperCase()}: ${msg}`); @@ -159,7 +179,7 @@ export class Emulator { this.log(`${level.toUpperCase()}: ${msg}`); // TODO: Fix this // This is really hacky solution, which depends on specific phrasing - if (msg.includes("Starting") && msg.includes(port)) { + if (msg.includes("Starting") && msg.includes(this.adminPort)) { this.log("EMULATOR IS UP! Listening for events!"); } } diff --git a/src/init.js b/src/init.js index 2201e06b..eafe17ad 100644 --- a/src/init.js +++ b/src/init.js @@ -17,7 +17,6 @@ */ import { set } from "./config"; -import { config } from "@onflow/config"; /** * Inits framework variables, storing private key of service account and base path @@ -27,7 +26,6 @@ import { config } from "@onflow/config"; * @param {number} [props.pkey] - private key to use for service account in case of collisions */ export const init = async (basePath, props = {}) => { - const { port } = props; const { pkey = "48a1f554aeebf6bf9fe0d7b5b79d080700b073ee77909973ea0b2f6fbc902" } = props; set("PRIVATE_KEY", process.env.PK, "accounts/emulator-account/key", pkey); @@ -38,9 +36,4 @@ export const init = async (basePath, props = {}) => { "f8d6e0586b0a20c7", ); set("BASE_PATH", process.env.BASE_PATH, "testing/paths", basePath); - - // Only set port if it was passed as argument - if (port) { - config().put("accessNode.api", `http://localhost:${port}`); - } }; diff --git a/src/utils.js b/src/utils.js index f3d7bec7..75922bbc 100644 --- a/src/utils.js +++ b/src/utils.js @@ -16,4 +16,20 @@ * limitations under the License. */ +import { createServer } from "net"; + export const isObject = (arg) => typeof arg === "object" && arg !== null; + +export function getAvailablePorts(count = 1) { + if (count === 0) return Promise.resolve([]); + return new Promise((resolve, reject) => { + const server = createServer(); + server.listen(0, () => { + const port = server.address().port; + server.close(async (err) => { + if (err) reject(err); + resolve([...(await getAvailablePorts(count - 1)), port]); + }); + }); + }); +}