Skip to main content

JS Testing API Reference

⚠️ Required: Your project must follow the required structure and it must be initialized to use the following functions.

Accounts

getAccountAddress

Resolves name alias to a Flow address (0x prefixed) under the following conditions:

  • If an account with a specific name has not been previously accessed, the framework will create a new one and then store it under the provided alias.
  • Next time when you call this method, it will grab exactly the same account. This allows you to create several accounts up-front and then use them throughout your code, without worrying that accounts match or trying to store and manage specific addresses.

Arguments

NameTypeDescription
aliasstringThe alias to reference or create.

Returns

TypeDescription
Address0x prefixed address of aliased account

Usage


_10
import {getAccountAddress} from "@onflow/flow-js-testing"
_10
_10
const main = async () => {
_10
const Alice = await getAccountAddress("Alice")
_10
console.log({Alice})
_10
}
_10
_10
main()

createAccount({name, keys})

In some cases, you may wish to manually create an account with a particular set of private keys

Options

Pass in the following as a single object with the following keys.

KeyTypeRequiredDescription
namestringNohuman-readable name to be associated with created account (will be used for address lookup within getAccountAddress)
keys[KeyObject or PublicKey]NoAn array of KeyObjects or PublicKeys to be added to the account upon creation (defaults to the universal private key)

📣 if name field not provided, the account address will not be cached and you will be unable to look it up using getAccountAddress.

Returns

TypeDescription
Address0x prefixed address of created account

Contracts

deployContractByName(props)

Deploys contract code located inside a Cadence file. Returns the transaction result.

Arguments

Props object accepts the following fields:

NameTypeOptionalDescription
namestringname of the file in contracts folder (with .cdc extension) and name of the contract (please note those should be the same)
toAddress(optional) account address, where contract will be deployed. If this is not specified, framework will create new account with randomized alias.
addressMapAddressMap(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.
updateboolean(optional) whether to update deployed contract. Default: false
transformers[CadenceTransformer](optional) an array of operators to modify the code, before submitting it to network

Returns

TypeDescription
ResponseObjectResult of the deploying transaction.

Usage


_27
import path from "path";
_27
import { init, emulator, deployContractByName } from "@onflow/flow-js-testing";
_27
_27
const main = async () => {
_27
const basePath = path.resolve(__dirname, "../cadence");
_27
_27
await init(basePath);
_27
await emulator.start();
_27
_27
// We will deploy our contract to the address that corresponds to "Alice" alias
_27
const to = await getAccountAddress("Alice");
_27
_27
// We assume there is a file on "../cadence/contracts/Wallet.cdc" path
_27
const name = "Wallet";
_27
_27
// Arguments will be processed and type matched in the same order as they are specified
_27
// inside of a contract template
_27
const args = [1337, "Hello", { name: "Alice" }];
_27
_27
const [deploymentResult, err] = await deployContractByName({ to, name });
_27
console.log({ deploymentResult }, { err });
_27
}
_27
_27
await emulator.stop();
_27
};
_27
_27
main();

In a bit more rare case you would want to deploy contract code not from existing template file, but rather from string representation of it. deployContract method will help you achieve this.

deployContract(props)

Deploys contract code specified as string. Returns the transaction result.

Arguments

Props object accepts the following fields:

NameTypeOptionalDescription
contractCodestringstring representation of contract
namestringname of the contract to be deployed. Should be the same as the name of the contract provided in contractCode
toAddressaccount address, where contract will be deployed. If this is not specified, framework will create new account with randomized alias.
addressMapAddressMapobject to use for import resolver. Default: {}
args[Any]arguments, which will be passed to contract initializer. Default: []
updatebooleanwhether to update deployed contract. Default: false
transformers[CadenceTransformer]an array of operators to modify the code, before submitting it to network

Returns

TypeDescription
ResponseObjectResult of the deploying transaction.

Usage


_42
import path from "path"
_42
import {
_42
init,
_42
emulator,
_42
getAccountAddress,
_42
deployContract,
_42
executeScript,
_42
} from "@onflow/flow-js-testing"
_42
;(async () => {
_42
const basePath = path.resolve(__dirname, "../cadence")
_42
_42
await init(basePath)
_42
await emulator.start()
_42
_42
// We can specify, which account will hold the contract
_42
const to = await getAccountAddress("Alice")
_42
_42
const name = "Wallet"
_42
const code = `
_42
access(all) contract Wallet{
_42
access(all) let balance: UInt
_42
init(balance: UInt){
_42
self.balance = balance
_42
}
_42
}
_42
`
_42
const args = [1337]
_42
_42
await deployContract({to, name, code, args})
_42
_42
const [balance, err] = await executeScript({
_42
code: `
_42
import Wallet from 0x01
_42
access(all) fun main(): UInt{
_42
return Wallet.balance
_42
}
_42
`,
_42
})
_42
console.log({balance}, {err})
_42
_42
await emulator.stop()
_42
})()

While framework have automatic import resolver for Contracts you might want to know where it's currently deployed. We provide a method getContractAddress for this.

getContractAddress(name)

Returns address of the account where the contract is currently deployed.

Arguments

NameTypeDescription
namestringname of the contract

Returns

TypeDescription
Address0x prefixed address

Usage


_17
import path from "path"
_17
import {init, emulator, deployContractByName, getContractAddress} from "../src"
_17
;(async () => {
_17
const basePath = path.resolve(__dirname, "./cadence")
_17
_17
await init(basePath)
_17
await emulator.start()
_17
_17
// if we omit "to" it will be deployed to Service Account
_17
// but let's pretend we don't know where it will be deployed :)
_17
await deployContractByName({name: "Hello"})
_17
_17
const contractAddress = await getContractAddress("Hello")
_17
console.log({contractAddress})
_17
_17
await emulator.stop()
_17
})()

📣 Framework does not support contracts with identical names deployed to different accounts. While you can deploy contract to a new address, the internal system, which tracks where contracts are deployed, will only store last address.

Cryptography

pubFlowKey(keyObject)

The pubFlowKey method exported by Flow JS Testing Library will generate an RLP-encoded public key given a private key, hashing algorithm, signing algorithm, and key weight.

NameTypeOptionalDescription
keyObjectKeyObjectan object containing a private key & the key's hashing/signing information

If keyObject is not provided, Flow JS Testing will default to the universal private key.

Returns

TypeDescription
BufferRLP-encoded public key

Usage


_11
import {pubFlowKey} from "@onflow/flow-js-testing"
_11
_11
const key = {
_11
privateKey: "a1b2c3", // private key as hex string
_11
hashAlgorithm: HashAlgorithm.SHA3_256,
_11
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256,
_11
weight: 1000,
_11
}
_11
_11
const pubKey = await pubFlowKey(key) // public key generated from keyObject provided
_11
const genericPubKey = await pubFlowKey() // public key generated from universal private key/service key

signUserMessage(msgHex, signer, domainTag)

The signUserMessage method will produce a user signature of some arbitrary data using a particular signer.

Arguments

NameTypeOptionalDescription
msgHexstring or Buffera hex-encoded string or Buffer which will be used to generate the signature
signerAddress or SignerInfoAddress or SignerInfo object representing user to generate this signature for (default: universal private key)
domainTagstringDomain separation tag provided as a utf-8 encoded string (default: no domain separation tag). See more about domain tags here.

Returns

TypeDescription
SignatureObjectAn object representing the signature for the message & account/keyId which signed for this message

Usage


_10
import {signUserMessage, getAccountAddress} from "@onflow/flow-js-testing"
_10
_10
const Alice = await getAccountAddress("Alice")
_10
const msgHex = "a1b2c3"
_10
_10
const signature = await generateUserSignature(msgHex, Alice)

verifyUserSigntatures(msgHex, signatures, domainTag)

Used to verify signatures generated by signUserMessage. This function takes an array of signatures and verifies that the total key weight sums to >= 1000 and that these signatures are valid.

Arguments

NameTypeOptionalDescription
msgHexstringthe message which the provided signatures correspond to provided as a hex-encoded string or Buffer
signatures[SignatureObject]An array of SignatureObjects which will be verified against this message
domainTagstringDomain separation tag provided as a utf-8 encoded string (default: no domain separation tag). See more about domain tags here.

Returns

This method returns an object with the following keys:

TypeDescription
booleanReturns true if signatures are valid and total weight >= 1000

Usage


_15
import {
_15
signUserMessage,
_15
verifyUserSignatures,
_15
getAccountAddress,
_15
} from "@onflow/flow-js-testing"
_15
_15
const Alice = await getAccountAddress("Alice")
_15
const msgHex = "a1b2c3"
_15
_15
const signature = await generateUserSignature(msgHex, Alice)
_15
_15
console.log(await verifyUserSignatures(msgHex, Alice)) // true
_15
_15
const Bob = await getAccountAddress("Bob")
_15
console.log(await verifyUserSignatures(msgHex, Bob)) // false

Emulator

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(options)

Starts emulator on a specified port. Returns Promise.

Arguments

NameTypeOptionalDescription
optionsEmulatorOptionsan object containing options for starting the emulator

EmulatorOptions

KeyTypeOptionalDescription
loggingbooleanwhether log messages from emulator shall be added to the output (default: false)
flagsstringcustom command-line flags to supply to the emulator (default: no flags)
adminPortnumberoverride the port which the emulator will run the admin server on (default: auto)
restPortnumberoverride the port which the emulator will run the REST server on (default: auto)
grpcPortnumberoverride the port which the emulator will run the GRPC server on (default: auto)
debuggerPortnumberoverride the port which the emulator will run the debug server on (default: auto)

Returns

TypeDescription
PromisePromise, which resolves to true if emulator started successfully

Usage


_15
import path from "path"
_15
import {emulator, init} from "../src"
_15
;(async () => {
_15
const basePath = path.resolve(__dirname, "../cadence")
_15
_15
await init(basePath)
_15
_15
// Start emulator instance on port 8080
_15
await emulator.start()
_15
console.log("emulator is working")
_15
_15
// Stop running emulator
_15
await emulator.stop()
_15
console.log("emulator has been stopped")
_15
})()

emulator.stop()

Stops emulator instance. Returns Promise.

Arguments

This method does not expect any arguments.

Returns

TypeDescription
PromisePromise, which resolves to true if emulator stopped without issues

Usage


_16
import {emulator, init} from "@onflow/flow-js-testing"
_16
_16
describe("test setup", () => {
_16
// Instantiate emulator and path to Cadence files
_16
beforeEach(async () => {
_16
const basePath = path.resolve(__dirname, "../cadence")
_16
_16
await init(basePath)
_16
await emulator.start()
_16
})
_16
_16
// Stop emulator, so it could be restarted
_16
afterEach(async () => {
_16
await emulator.stop()
_16
})
_16
})

emulator.setLogging(newState)

Set logging flag on emulator, allowing to temporally enable/disable logging.

Arguments

NameTypeDescription
newStatebooleanEnable/disable logging

Returns

Method does not return anything.

Usage


_27
import path from "path"
_27
import {emulator, init} from "@onflow/flow-js-testing"
_27
_27
describe("test setup", () => {
_27
// Instantiate emulator and path to Cadence files
_27
beforeEach(async () => {
_27
const basePath = path.resolve(__dirname, "../cadence")
_27
_27
await init(basePath)
_27
await emulator.start()
_27
})
_27
_27
// Stop emulator, so it could be restarted
_27
afterEach(async () => {
_27
await emulator.stop()
_27
})
_27
_27
test("basic test", async () => {
_27
// Turn on logging from begining
_27
emulator.setLogging(true)
_27
// some asserts and interactions
_27
_27
// Turn off logging for later calls
_27
emulator.setLogging(false)
_27
// more asserts and interactions here
_27
})
_27
})

FLOW Management

Some actions on the network will require account to have certain amount of FLOW token - transaction and storage fees, account creation, etc.

Framework provides a method to query balance with getFlowBalance and mint new tokens via mintFlow. You can find information how to use them below.

getFlowBalance(address)

Fetch current FlowToken balance of account specified by address

Arguments

NameTypeDescription
addressAddressaddress of the account to check

Returns

TypeDescription
stringUFix64 amount of FLOW tokens stored in account storage represented as string

Usage


_22
import {
_22
init,
_22
emulator,
_22
getAccountAddress,
_22
getFlowBalance,
_22
} from "@onflow/flow-js-testing"
_22
_22
const main = async () => {
_22
const basePath = path.resolve(__dirname, "../cadence")
_22
_22
await init(basePath)
_22
await emulator.start()
_22
_22
const Alice = await getAccountAddress("Alice")
_22
_22
const [result, error] = await getFlowBalance(Alice)
_22
console.log({result}, {error})
_22
_22
await emulator.stop()
_22
}
_22
_22
main()

mintFlow(recipient, amount)

Sends transaction to mint specified amount of FLOW token and send it to recipient.

⚠️ Required: Framework shall be initialized with init method for this method to work.

Arguments

NameTypeDescription
recipientAddressaddress of the account to check
amountstringUFix64 amount of FLOW tokens to mint and send to recipient

Returns

TypeDescription
ResponseObjectTransaction result

Usage


_30
import path from "path"
_30
import {
_30
init,
_30
emulator,
_30
getAccountAddress,
_30
getFlowBalance,
_30
mintFlow,
_30
} from "../src"
_30
;(async () => {
_30
const basePath = path.resolve(__dirname, "./cadence")
_30
_30
await init(basePath)
_30
await emulator.start()
_30
_30
// Get address for account with alias "Alice"
_30
const Alice = await getAccountAddress("Alice")
_30
_30
// Get initial balance
_30
const [initialBalance] = await getFlowBalance(Alice)
_30
console.log(initialBalance)
_30
_30
// Add 1.0 FLOW tokens to Alice account
_30
await mintFlow(Alice, "1.0")
_30
_30
// Check updated balance
_30
const updatedBalance = await getFlowBalance(Alice)
_30
console.log({updatedBalance})
_30
_30
await emulator.stop()
_30
})()

Init

For Framework to operate properly you need to initialize it first. You can do it with provided init method.

init( basePath, options)

Initializes framework variables.

Arguments

NameTypeOptionalDescription
bastPathstringpath to the folder holding all Cadence template files
optionsobjectoptions object to use during initialization

Options

NameTypeOptionalDescription
pkeyprivate key for service account

Returns

TypeDescription
PromisePromise, which resolves to true if framework was initialized properly

Usage


_12
import path from "path"
_12
import {init} from "@onflow/flow-js-testing"
_12
_12
describe("test setup", () => {
_12
beforeEach(async () => {
_12
const basePath = path.resolve(__dirname, "../cadence")
_12
await init(basePath)
_12
_12
// alternatively you can pass specific port
_12
// await init(basePath, {port: 8085})
_12
})
_12
})

Environment

getBlockOffset()

Returns current block offset - amount of blocks added on top of real current block height.

Returns

TypeDescription
stringnumber representing amount of blocks added on top of real current block (encoded as string)

Usage


_16
import path from "path"
_16
import {init, emulator, getBlockOffset} from "@onflow/flow-js-testing"
_16
_16
const main = async () => {
_16
const basePath = path.resolve(__dirname, "../cadence")
_16
_16
init(basePath)
_16
await emulator.start()
_16
_16
const [blockOffset, err] = await getBlockOffset()
_16
console.log({blockOffset}, {err})
_16
_16
await emulator.stop()
_16
}
_16
_16
main()

setBlockOffset(offset)

Returns current block offset - amount of blocks added on top of real current block height.

Arguments

NameTypeDescription

Returns

TypeDescription
numbernumber representing amount of blocks added on top of real current block height

Usage


_36
import path from "path"
_36
import {
_36
init,
_36
emulator,
_36
executeScript,
_36
getBlockOffset,
_36
setBlockOffset,
_36
sendTransaction,
_36
} from "@onflow/flow-js-testing"
_36
_36
const main = async () => {
_36
const basePath = path.resolve(__dirname, "../cadence")
_36
_36
init(basePath)
_36
await emulator.start()
_36
_36
// Offset current block height by 42
_36
await setBlockOffset(42)
_36
_36
const [blockOffset, err] = await getBlockOffset()
_36
console.log({blockOffset}, {err})
_36
_36
// "getCurrentBlock().height" in your Cadence code will be replaced by Manager to a mocked value
_36
const code = `
_36
access(all) fun main(): UInt64 {
_36
return getCurrentBlock().height
_36
}
_36
`
_36
_36
const [result, error] = await executeScript({code})
_36
console.log({result}, {error})
_36
_36
await emulator.stop()
_36
}
_36
_36
main()

getTimestampOffset()

Returns current timestamp offset - amount of seconds added on top of real current timestamp.

Returns

TypeDescription
numbernumber representing amount of seconds added on top of real current timestamp

Usage


_16
import path from "path"
_16
import {init, emulator, getTimestampOffset} from "@onflow/flow-js-testing"
_16
_16
const main = async () => {
_16
const basePath = path.resolve(__dirname, "../cadence")
_16
_16
init(basePath)
_16
await emulator.start()
_16
_16
const [timestampOffset, err] = await getTimestampOffset()
_16
console.log({timestampOffset}, {err})
_16
_16
await emulator.stop()
_16
}
_16
_16
main()

setTimestampOffset(offset)

Returns current timestamp offset - amount of seconds added on top of real current timestamp.

Arguments

NameTypeDescription

Returns

TypeDescription
numbernumber representing amount of seconds added on top of real current timestamp

Usage


_36
import path from "path"
_36
import {
_36
init,
_36
emulator,
_36
executeScript,
_36
getTimestampOffset,
_36
setTimestampOffset,
_36
sendTransaction,
_36
} from "@onflow/flow-js-testing"
_36
_36
const main = async () => {
_36
const basePath = path.resolve(__dirname, "../cadence")
_36
_36
init(basePath)
_36
await emulator.start()
_36
_36
// Offset current timestamp by 10s
_36
await setTimestampOffset(10)
_36
_36
const [timestampOffset, err] = await getTimestampOffset()
_36
console.log({timestampOffset}, {err})
_36
_36
// "getCurrentBlock().timestamp" in your Cadence code will be replaced by Manager to a mocked value
_36
const code = `
_36
access(all) fun main(): UInt64 {
_36
return getCurrentBlock().timestamp
_36
}
_36
`
_36
_36
const [result, error] = await executeScript({code})
_36
console.log({result}, {error})
_36
_36
await emulator.stop()
_36
}
_36
_36
main()

Jest Helpers

In order to simplify the process even further we've created several Jest-based methods, which will help you to catch thrown errors and ensure your code works as intended.

shallPass(ix)

Ensure transaction does not throw and sealed.

Arguments

NameTypeDescription
ixInteractioninteraction, either in form of a Promise or function

Returns

TypeDescription
ResponseObjectTransaction result

Usage


_49
import path from "path"
_49
import {
_49
init,
_49
emulator,
_49
shallPass,
_49
sendTransaction,
_49
getAccountAddress,
_49
} from "js-testing-framework"
_49
_49
// We need to set timeout for a higher number, cause some interactions might need more time
_49
jest.setTimeout(10000)
_49
_49
describe("interactions - sendTransaction", () => {
_49
// Instantiate emulator and path to Cadence files
_49
beforeEach(async () => {
_49
const basePath = path.resolve(__dirname, "./cadence")
_49
await init(basePath)
_49
return emulator.start()
_49
})
_49
_49
// Stop emulator, so it could be restarted
_49
afterEach(async () => {
_49
return emulator.stop()
_49
})
_49
_49
test("basic transaction", async () => {
_49
const code = `
_49
transaction(message: String){
_49
prepare(singer: AuthAccount){
_49
log(message)
_49
}
_49
}
_49
`
_49
const Alice = await getAccountAddress("Alice")
_49
const signers = [Alice]
_49
const args = ["Hello, Cadence"]
_49
_49
const [txResult, error] = await shallPass(
_49
sendTransaction({
_49
code,
_49
signers,
_49
args,
_49
})
_49
)
_49
_49
// Transaction result will hold status, events and error message
_49
console.log({txResult}, {error})
_49
})
_49
})

shallRevert(ix, message)

Ensure interaction throws an error. Can test for specific error messages or catch any error message if message is not provided. Returns Promise, which contains result, when resolved.

Arguments

NameTypeDescription
ixInteractiontransaction, either in form of a Promise or function
message (optional)string or RegExpexpected error message provided as either a string equality or regular expression to match, matches any error by default

Returns

TypeDescription
ResponseObjectTransaction result

Usage


_60
import path from "path"
_60
import {
_60
init,
_60
emulator,
_60
shallPass,
_60
sendTransaction,
_60
getAccountAddress,
_60
} from "js-testing-framework"
_60
_60
// We need to set timeout for a higher number, cause some interactions might need more time
_60
jest.setTimeout(10000)
_60
_60
describe("interactions - sendTransaction", () => {
_60
// Instantiate emulator and path to Cadence files
_60
beforeEach(async () => {
_60
const basePath = path.resolve(__dirname, "./cadence")
_60
await init(basePath)
_60
return emulator.start()
_60
})
_60
_60
// Stop emulator, so it could be restarted
_60
afterEach(async () => {
_60
return emulator.stop()
_60
})
_60
_60
test("basic transaction", async () => {
_60
const code = `
_60
transaction(message: String){
_60
prepare(singer: AuthAccount){
_60
panic("You shall not pass!")
_60
}
_60
}
_60
`
_60
const Alice = await getAccountAddress("Alice")
_60
const signers = [Alice]
_60
const args = ["Hello, Cadence"]
_60
_60
// Catch any cadence error
_60
let [txResult, error] = await shallRevert(
_60
sendTransaction({
_60
code,
_60
signers,
_60
args,
_60
})
_60
)
_60
_60
// Catch only specific panic message
_60
let [txResult2, error2] = await shallRevert(
_60
sendTransaction({
_60
code,
_60
signers,
_60
args,
_60
}),
_60
"You shall not pass!"
_60
)
_60
_60
// Transaction result will hold status, events and error message
_60
console.log({txResult}, {error})
_60
})
_60
})

shallResolve(ix)

Ensure interaction resolves without throwing errors.

Arguments

NameTypeDescription
ixInteractioninteraction, either in form of a Promise or function

Returns

TypeDescription
InteractionResultInteraction result

Usage


_36
import path from "path"
_36
import {init, emulator, shallPass, executeScript} from "js-testing-framework"
_36
_36
// We need to set timeout for a higher number, cause some interactions might need more time
_36
jest.setTimeout(10000)
_36
_36
describe("interactions - sendTransaction", () => {
_36
// Instantiate emulator and path to Cadence files
_36
beforeEach(async () => {
_36
const basePath = path.resolve(__dirname, "./cadence")
_36
await init(basePath)
_36
return emulator.start()
_36
})
_36
_36
// Stop emulator, so it could be restarted
_36
afterEach(async () => {
_36
return emulator.stop()
_36
})
_36
_36
test("basic script", async () => {
_36
const code = `
_36
access(all) fun main():Int{
_36
return 42
_36
}
_36
`
_36
_36
const [result, error] = await shallResolve(
_36
executeScript({
_36
code,
_36
})
_36
)
_36
_36
expect(result).toBe(42)
_36
expect(error).toBe(null)
_36
})
_36
})

Scripts

It is often the case that you need to query current state of the network. For example, to check balance of the account, read public value of the contract or ensure that user has specific resource in their storage.

We abstract this interaction into single method called executeScript. Method have 2 different signatures.

⚠️ Required: Your project must follow the required structure it must be initialized to use the following functions.

executeScript(props)

Provides explicit control over how you pass values.

Arguments

props object accepts following fields:

NameTypeOptionalDescription
codestringstring representation of Cadence script
namestringname of the file in scripts folder to use (sans .cdc extension)
args[any]an array of arguments to pass to script. Optional if script does not expect any arguments.
transformers[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.

Returns

TypeDescription
ResponseObjectScript result

Usage


_29
import path from "path"
_29
import {init, emulator, executeScript} from "@onflow/flow-js-testing"
_29
_29
const main = async () => {
_29
const basePath = path.resolve(__dirname, "../cadence")
_29
_29
// Init framework
_29
init(basePath)
_29
// Start emulator
_29
await emulator.start()
_29
_29
// Define code and arguments we want to pass
_29
const code = `
_29
access(all) fun main(message: String): Int{
_29
log(message)
_29
_29
return 42
_29
}
_29
`
_29
const args = ["Hello, from Cadence"]
_29
_29
const [result, error, logs] = await executeScript({code, args})
_29
console.log({result}, {error}, {logs})
_29
_29
// Stop emulator instance
_29
await emulator.stop()
_29
}
_29
_29
main()

executeScript(name: string, args: [any])

This signature provides simplified way of executing a script, since most of the time you will utilize existing Cadence files.

Arguments

NameTypeOptionalDescription
namestringname of the file in scripts folder to use (sans .cdc extension)
args[any]an array of arguments to pass to script. Optional if scripts don't expect any arguments. Default: []

Returns

TypeDescription
ResponseObjectScript result

Usage


_22
import path from "path"
_22
import {init, emulator, executeScript} from "@onflow/flow-js-testing"
_22
_22
const main = async () => {
_22
const basePath = path.resolve(__dirname, "../cadence")
_22
_22
// Init framework
_22
init(basePath)
_22
// Start emulator
_22
await emulator.start()
_22
_22
// Define arguments we want to pass
_22
const args = ["Hello, from Cadence"]
_22
_22
// We assume there is a file `scripts/log-message.cdc` under base path
_22
const [result, error, logs] = await executeScript("log-message", args)
_22
console.log({result}, {error}, {logs})
_22
_22
await emulator.stop()
_22
}
_22
_22
main()

Transactions

Another common case is interactions that mutate network state - sending tokens from one account to another, minting new NFT, etc. Framework provides sendTransaction method to achieve this. This method have 2 different signatures.

⚠️ Required: Your project must follow the required structure it must be initialized to use the following functions.

sendTransaction(props)

Send transaction to network. Provides explicit control over how you pass values.

Arguments

props object accepts following fields:

NameTypeOptionalDescription
codestringstring representation of Cadence transaction
namestringname 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 or SignerInfo]an array of Address or SignerInfo objects representing transaction autorizers
addressMapAddressMapname/address map to use as lookup table for addresses in import statements
transformers[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.

📣 if signers field not provided, service account will be used to authorize the transaction.

📣 Pass addressMap only in cases, when you would want to override deployed contract. Otherwide imports can be resolved automatically without explicitly passing them via addressMap field

Usage


_36
import path from "path"
_36
import {
_36
init,
_36
emulator,
_36
sendTransaction,
_36
getAccountAddress,
_36
} from "@onflow/flow-js-testing"
_36
_36
const main = async () => {
_36
const basePath = path.resolve(__dirname, "../cadence")
_36
_36
// Init framework
_36
await init(basePath)
_36
// Start emulator
_36
await emulator.start()
_36
_36
// Define code and arguments we want to pass
_36
const code = `
_36
transaction(message: String){
_36
prepare(signer: AuthAccount){
_36
log(message)
_36
}
_36
}
_36
`
_36
const args = ["Hello, from Cadence"]
_36
const Alice = await getAccountAddress("Alice")
_36
const signers = [Alice]
_36
_36
const [result, error, logs] = await sendTransaction({code, args, signers})
_36
console.log({result}, {error}, {logs})
_36
_36
// Stop emulator instance
_36
await emulator.stop()
_36
}
_36
_36
main()

sendTransaction(name, signers, args)

This signature provides simplified way to send a transaction, since most of the time you will utilize existing Cadence files.

NameTypeOptionalDescription
namestringname 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 or SignerInfoObject]an array of Address or array of SignerInfoObject representing transaction autorizers

Usage


_29
import path from "path"
_29
import {
_29
init,
_29
emulator,
_29
sendTransaction,
_29
shallPass,
_29
} from "@onflow/flow-js-testing"
_29
_29
const main = async () => {
_29
const basePath = path.resolve(__dirname, "../cadence")
_29
_29
// Init framework
_29
await init(basePath)
_29
// Start emulator
_29
await emulator.start()
_29
_29
// Define arguments we want to pass
_29
const args = ["Hello, Cadence"]
_29
_29
const [result, error, logs] = await shallPass(
_29
sendTransaction("log-message", [], args)
_29
)
_29
console.log({result}, {error}, {logs})
_29
_29
// Stop the emulator instance
_29
await emulator.stop()
_29
}
_29
_29
main()

Templates

The philosophy behind Flow JS Testing Framework is to be a set of helper methods. They can be used in opinionated way, envisioned by Flow Team. Or they can work as building blocks, allowing developers to build their own testing solution as they see fit.

Following methods used inside other framework methods, but we feel encouraged to list them here as well.

getTemplate(file, addressMap, byAddress)

Returns Cadence template as string with addresses replaced using addressMap

NameTypeOptionalDescription
filestringrelative (to the place from where the script was called) or absolute path to the file containing the code
addressMapAddressMapobject to use for address mapping of existing deployed contracts. Default: {}
byAddressbooleanwhether addressMap is {name:address} or {address:address} type. Default: false

Returns

TypeDescription
stringcontent of a specified file

Usage


_12
import path from "path"
_12
import {init, getTemplate} from "@onflow/flow-js-testing"
_12
_12
const main = async () => {
_12
const basePath = path.resolve(__dirname, "../cadence")
_12
await init(basePath)
_12
_12
const template = await getTemplate("../cadence/scripts/get-name.cdc")
_12
console.log({template})
_12
}
_12
_12
main()

getContractCode(name, addressMap)

Returns Cadence template from file with name in _basepath_/contracts folder

Arguments

NameTypeOptionalDescription
namestringname of the contract template
addressMapAddressMapobject to use for address mapping of existing deployed contracts

Returns

TypeDescription
stringCadence template code for specified contract

Usage


_23
import path from "path"
_23
import {init, emulator, getContractCode} from "@onflow/flow-js-testing"
_23
_23
const main = async () => {
_23
const basePath = path.resolve(__dirname, "../cadence")
_23
_23
await init(basePath)
_23
await emulator.start()
_23
_23
// Let's assume we need to import MessageContract
_23
await deployContractByName({name: "MessageContract"})
_23
const [MessageContract] = await getContractAddress("MessageContract")
_23
const addressMap = {MessageContract}
_23
_23
const contractTemplate = await getContractCode("HelloWorld", {
_23
MessageContract,
_23
})
_23
console.log({contractTemplate})
_23
_23
await emulator.stop()
_23
}
_23
_23
main()

getTransactionCode(name, addressMap)

Returns Cadence template from file with name in _basepath_/transactions folder

Arguments

NameTypeOptionalDescription
namestringname of the transaction template
addressMapAddressMapobject to use for address mapping of existing deployed contracts

Returns

TypeDescription
stringCadence template code for specified transaction

Usage


_24
import path from "path"
_24
import {init, emulator, getTransactionCode} from "@onflow/flow-js-testing"
_24
_24
const main = async () => {
_24
const basePath = path.resolve(__dirname, "../cadence")
_24
_24
await init(basePath)
_24
await emulator.start()
_24
_24
// Let's assume we need to import MessageContract
_24
await deployContractByName({name: "MessageContract"})
_24
const [MessageContract] = await getContractAddress("MessageContract")
_24
const addressMap = {MessageContract}
_24
_24
const txTemplate = await getTransactionCode({
_24
name: "set-message",
_24
addressMap,
_24
})
_24
console.log({txTemplate})
_24
_24
await emulator.stop()
_24
}
_24
_24
main()

getScriptCode(name, addressMap)

Returns Cadence template from file with name in _basepath_/scripts folder

Arguments

NameTypeOptionalDescription
namestringname of the script template
addressMapAddressMapobject to use for address mapping of existing deployed contracts

Returns

TypeDescription
stringCadence template code for specified script

Usage


_24
import path from "path"
_24
import {init, emulator, getScriptCode} from "@onflow/flow-js-testing"
_24
_24
const main = async () => {
_24
const basePath = path.resolve(__dirname, "../cadence")
_24
_24
await init(basePath)
_24
await emulator.start()
_24
_24
// Let's assume we need to import MessageContract
_24
await deployContractByName({name: "MessageContract"})
_24
const [MessageContract] = await getContractAddress("MessageContract")
_24
const addressMap = {MessageContract}
_24
_24
const scriptTemplate = await getScriptCode({
_24
name: "get-message",
_24
addressMap,
_24
})
_24
_24
console.log({scriptTemplate})
_24
await emulator.stop()
_24
}
_24
_24
main()

Storage Inspection

getPaths

Retrieves information about the public, private, and storage paths for a given account.

Arguments

NameTypeDescription
addressstringThe address or name of the account to retrieve the paths from.
useSet (optional)booleanWhether to return the paths as a Set or an array. Defaults to true.

Returns

An object containing the following properties:

NameTypeDescription
publicPathsArray<string> or Set<string>An array or Set of the public paths for the account, as strings.
privatePathsArray<string> or Set<string>An array or Set of the private paths for the account, as strings.
storagePathsArray<string> or Set<string>An array or Set of the storage paths for the account, as strings.

The useSet parameter determines whether the paths are returned as an array or Set. If useSet is true, the paths will be returned as a Set; otherwise, they will be returned as an array.

Usage


_22
import path from "path"
_22
import {init, emulator} from "@onflow/flow-js-testing"
_22
import {getAccountAddress, getPaths} from "@onflow/flow-js-testing"
_22
_22
const main = async () => {
_22
const basePath = path.resolve(__dirname, "../cadence")
_22
_22
await init(basePath)
_22
await emulator.start()
_22
_22
// Get storage stats
_22
const Alice = await getAccountAddress("Alice")
_22
const paths = await getPaths(Alice)
_22
const {publicPaths, privatePaths, storagePaths} = paths
_22
_22
// Output result to console
_22
console.log({Alice, paths})
_22
_22
await emulator.stop()
_22
}
_22
_22
main()

getPathsWithType

Retrieves public, private, and storage paths for a given account with extra information available on them

Arguments

NameTypeDescription
addressstringThe address or name of the account to retrieve the paths from.

Returns

An object containing the following properties:

NameTypeDescription
publicPathsObjectAn object containing the public paths for the account, as keys and their types as values.
privatePathsObjectAn object containing the private paths for the account, as keys and their types as values.
storagePathsObjectAn object containing the storage paths for the account, as keys and their types as values.

The types of the paths are not strictly defined and may vary depending on the actual types used in the account.

Usage


_29
import path from "path"
_29
import {init, emulator} from "@onflow/flow-js-testing"
_29
import {getPathsWithType} from "@onflow/flow-js-testing"
_29
_29
const main = async () => {
_29
const basePath = path.resolve(__dirname, "../cadence")
_29
_29
await init(basePath)
_29
await emulator.start()
_29
_29
const {publicPaths} = await getPathsWithType("Alice")
_29
const refTokenBalance = publicPaths.flowTokenBalance
_29
_29
if (
_29
refTokenBalance.restrictionsList.has(
_29
"A.ee82856bf20e2aa6.FungibleToken.Balance"
_29
)
_29
) {
_29
console.log("Found specific restriction")
_29
}
_29
_29
if (refTokenBalance.haveRestrictions("FungibleToken.Balance")) {
_29
console.log("Found matching restriction")
_29
}
_29
_29
await emulator.stop()
_29
}
_29
_29
main()

getStorageValue

Arguments

NameTypeDescription
accountstringThe address or name of the account to retrieve the storage value from.
pathstringThe path of the storage value to retrieve.

Returns

TypeDescription
Promise<any>The value of the storage at the given path, or null if no value is found.

Usage


_28
import path from "path"
_28
import {init, emulator} from "@onflow/flow-js-testing"
_28
import {sendTransaction, getStorageValue} from "@onflow/flow-js-testing"
_28
_28
const main = async () => {
_28
const basePath = path.resolve(__dirname, "../cadence")
_28
_28
await init(basePath)
_28
await emulator.start()
_28
_28
// Inplant some value into account
_28
await sendTransaction({
_28
code: `
_28
transaction{
_28
prepare(signer: AuthAccount){
_28
signer.save(42, to: /storage/answer)
_28
}
_28
}
_28
`,
_28
signers: [Alice],
_28
})
_28
const answer = await getStorageValue("Alice", "answer")
_28
console.log({answer})
_28
_28
await emulator.stop()
_28
}
_28
_28
main()

getStorageStats

Retrieves the storage statistics (used and capacity) for a given account.

Arguments

NameTypeDescription
addressstringThe address or name of the account to check for storage statistics.
convert (optional)booleanWhether to convert the used and capacity values from strings to numbers. Defaults to true.

Returns

A Promise that resolves to an object containing the following properties:

NameTypeDescription
usednumber or stringThe amount of storage used by the account, in bytes.
capacitynumber or stringThe total storage capacity of the account, in bytes.

If convert is true, the used and capacity values will be converted from strings to numbers before being returned.

Usage


_21
import path from "path"
_21
import {init, emulator} from "@onflow/flow-js-testing"
_21
import {getAccountAddress, getStorageStats} from "@onflow/flow-js-testing"
_21
_21
const main = async () => {
_21
const basePath = path.resolve(__dirname, "../cadence")
_21
_21
await init(basePath)
_21
await emulator.start()
_21
_21
// Get storage stats
_21
const Alice = await getAccountAddress("Alice")
_21
const {capacity, used} = await getStorageStats(Alice)
_21
_21
// Output result to console
_21
console.log({Alice, capacity, used})
_21
_21
await emulator.stop()
_21
}
_21
_21
main()

Types

AddressMap

Object to use for address mapping of existing deployed contracts. Key shall be string and value shall be Address

Example


_10
const addressMap = {
_10
Messanger: "0x01cf0e2f2f715450",
_10
Logger: "0x179b6b1cb6755e31",
_10
}

Interaction

Interaction is a Promise or function returning a promise.

Example


_10
const ix = async () => {
_10
return new Promise((resolve, reject) => {
_10
setTimeout(() => {
_10
resolve(1337)
_10
})
_10
}, 500)
_10
}

CadenceTransformer

Function, which will get valid Cadence code, modify it and return valid Cadence code

Example

This transformer will look for occupancies of specific import statement and replace it with proper address, where it's deployed on Emulator


_10
const replaceAddress = async code => {
_10
const modified = code.replace(
_10
/import\s+FungibleToken\s+from\s+0xFUNGIBLETOKEN/,
_10
"import FungibleToken from 0xee82856bf20e2aa6"
_10
)
_10
_10
return modified
_10
}

KeyObject

Key objects are used to specify signer keys when creating accounts.

KeyRequiredValue TypeDescription
hashAlgorithmNoHashAlgorithmHashing algorithm to use for generating signatures to be signed by this key (default: HashAlgorithm.SHA3_256)
privateKeyYesstringPrivate key to use to generate the signature
signatureAlgorithmNoSignatureAlgorithmSigning algorithm used to sign transactions with this key (default: SignatureAlgorithm.ECDSA_P256)
weightNonumberWeight of the key - see Flow Core Concepts for more information

PublicKey

Public keys are stored as Buffer objects which have been RLP encoded according to the Flow spec.

In order to generate this object using the Flow JS Testing library, use the pubFlowKey method exported by the library.


_10
import {pubFlowKey} from "@onflow/flow-js-testing"
_10
_10
const pubKey = await pubFlowKey({
_10
privateKey: ...,
_10
hashAlgorithm: ...,
_10
signatureAlgorithm: ...,
_10
weight: ...
_10
})

SignatureObject

Signature objects are used to represent a signature for a particular message as well as the account and keyId which signed for this message.

KeyValue TypeDescription
addrAddressthe address of the account which this signature has been generated for
keyIdnumberAddress or SignerInfo object representing user to generate this signature for
signaturestringa hexidecimal-encoded string representation of the generated signature

SignerInfoObject

Signer Info objects are used to specify information about which signer and which key from this signer shall be used to sign a transaction.

KeyRequiredValue TypeDescription
addrYesAddressThe address of the signer's account
hashAlgorithmNoHashAlgorithmHashing algorithm to use for generating the signature (default: HashAlgorithm.SHA3_256)
keyIdNonumberThe index of the desired key to use from the signer's account (default: 0)
privateKeyNostringPrivate key to use to generate the signature (default: service account private key - this is the default PK for all accounts generated by Flow JS Testing Library, see: accounts)
signatureAlgorithmNoSignatureAlgorithmSigning algorithm used to generate the signature (default: SignatureAlgorithm.ECDSA_P256)

HashAlgorithm

IdentifierValue
SHA2_2561
SHA3_2563

Hash algorithms may be provided as either an enum (accessible via the HashAlgorithm object exported by Flow JS Testing, i.e. HashAlgorithm.SHA3_256) or as a string representation of their enum identifier (i.e. "SHA3_256")

SignatureAlgorithm

IdentifierValue
ECDSA_P2562
ECDSA_secp256k13

Signing algorithms may be provided as either an enum (accessible via the SignatureAlgorithm object exported by Flow JS Testing, i.e. SignatureAlgorithm.ECDSA_P256) or as a string representation of their enum identifier (i.e. "ECDSA_P256")

Utilities

isAddress(address)

Returns true if the given string is a validly formatted account address (both "0x" prefixed and non-prefixed are valid)

Arguments

NameTypeOptionalDescription
addressstringstring to test against the regex

Returns

TypeDescription
booleanReturns true if given string is a validly formatted account address.

Usage


_10
import {isAddress} from "@onflow/flow-js-testing"
_10
_10
const badAddr = "0xqrtyff"
_10
console.log(isAddress(badAddr)) // false
_10
_10
const goodAddrWithPrefix = "0xf8d6e0586b0a20c1"
_10
console.log(isAddress(goodAddrWithPrefix)) // true
_10
_10
const goodAddrSansPrefix = "f8d6e0586b0a20c1"
_10
console.log(isAddress(goodAddrSansPrefix)) // true