@onflow/flow-js-testing
Version:
This package will expose a set of utility methods, to allow Cadence code testing with libraries like Jest
159 lines (135 loc) • 5.57 kB
JavaScript
/*
* Flow JS Testing
*
* Copyright 2020-2021 Dapper Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from "fs"
import path from "path"
import {config} from "@onflow/fcl"
import {fixShorthandImports, replaceImportAddresses} from "./imports"
import {isObject} from "./utils"
export const readFile = path => {
const code = fs.readFileSync(path, "utf8")
// TODO remove once flow-cadut is updated to support short-hand imports
return fixShorthandImports(code)
}
/**
* Address map with access by name for contracts deployed to emulator by default.
* @type {{FlowFees: string, FlowToken: string, FungibleToken: string}}
*/
export const defaultsByName = {
FlowToken: "0x0ae53cb6e3f42a79",
FungibleToken: "0xee82856bf20e2aa6",
FungibleTokenMetadataViews: "0xee82856bf20e2aa6",
FungibleTokenSwitchboard: "0xee82856bf20e2aa6",
FlowFees: "0xe5a8b7f23e8b548f",
FlowStorageFees: "0xf8d6e0586b0a20c7",
FlowIDTableStaking: "0xf8d6e0586b0a20c7",
FlowEpoch: "0xf8d6e0586b0a20c7",
FlowClusterQC: "0xf8d6e0586b0a20c7",
FlowDKG: "0xf8d6e0586b0a20c7",
FlowStakingCollection: "0xf8d6e0586b0a20c7",
FlowServiceAccount: "0xf8d6e0586b0a20c7",
RandomBeaconHistory: "0xf8d6e0586b0a20c7",
NodeVersionBeacon: "0xf8d6e0586b0a20c7",
EVM: "0xf8d6e0586b0a20c7",
FUSD: "0xf8d6e0586b0a20c7",
NonFungibleToken: "0xf8d6e0586b0a20c7",
MetadataViews: "0xf8d6e0586b0a20c7",
ViewResolver: "0xf8d6e0586b0a20c7",
NFTStorefront: "0xf8d6e0586b0a20c7",
NFTStorefrontV2: "0xf8d6e0586b0a20c7",
}
/**
* Address map with access by address for contracts deployed to emulator by default.
* @type {{"0xe5a8b7f23e8b548f": string, "0xf8d6e0586b0a20c7": string, "0xee82856bf20e2aa6": string, "0x0ae53cb6e3f42a79": string}}
*/
export const defaultsByAddress = {
"0xe5a8b7f23e8b548f": "0xe5a8b7f23e8b548f", // FlowFees
"0xf8d6e0586b0a20c7": "0xf8d6e0586b0a20c7", // FlowStorageFees
"0x0ae53cb6e3f42a79": "0x0ae53cb6e3f42a79", // FlowToken
"0xee82856bf20e2aa6": "0xee82856bf20e2aa6", // FungibleToken
}
const SCRIPT = "scripts"
const TRANSACTION = "transactions"
const CONTRACT = "contracts"
export const templateType = {
SCRIPT,
TRANSACTION,
CONTRACT,
}
export const getPath = async (name, type = TRANSACTION) => {
const configBase = await config().get("BASE_PATH")
// We can simply overwrite "configBase" variable, but I believe it's better to leave it unchanged
let basePath = configBase
// It's possible to pass a set of paths via object, so we need to check if that's the case
if (isObject(configBase)) {
const typePath = configBase[type]
// if there is a specific path for this type, then we shall resolve it
if (typePath) {
return path.resolve(typePath, `./${name}.cdc`)
}
// otherwise use "base" value
basePath = configBase.base
}
return path.resolve(basePath, `./${type}/${name}.cdc`)
}
/**
* Returns Cadence template for specified file. Replaces imports using provided address map
* @param file - name of the file to look for.
* @param {{string:string}} [addressMap={}] - name/address map to use as lookup table for addresses in import statements.
* @param {boolean} [byAddress=false] - flag to indicate if address map is address to address type.
* @returns {string}
*/
export const getTemplate = (file, addressMap = {}, byAddress = false) => {
const rawCode = readFile(file)
const defaults = byAddress ? defaultsByAddress : defaultsByName
return addressMap
? replaceImportAddresses(rawCode, {
...defaults,
...addressMap,
})
: rawCode
}
/**
* Returns contract template using name of the file in "contracts" folder containing the code.
* @param name - name of the contract template in "contract" folder.
* @param {{string:string}} [addressMap={}] - name/address map to use as lookup table for addresses in import statements.
* @returns {Promise<string>}
*/
export const getContractCode = async ({name, addressMap}) => {
const path = await getPath(name, templateType.CONTRACT)
return getTemplate(path, addressMap)
}
/**
* Returns transaction template using name of the file in "transactions" folder containing the code.
* @param name - name of the transaction template in "transactions" folder.
* @param {{string:string}} [addressMap={}] - name/address map to use as lookup table for addresses in import statements.
* @returns {Promise<string>}
*/
export const getTransactionCode = async ({name, addressMap}) => {
const path = await getPath(name, templateType.TRANSACTION)
return getTemplate(path, addressMap)
}
/**
* Returns script template using name of the file in "scripts" folder containing the code.
* @param name - name of the script template in "scripts" folder.
* @param {{string:string}} [addressMap={}] - name/address map to use as lookup table for addresses in import statements.
* @returns {Promise<string>}
*/
export const getScriptCode = async ({name, addressMap}) => {
const path = await getPath(name, templateType.SCRIPT)
return getTemplate(path, addressMap)
}