Error: call revert exception on Hardhat with Mocha,Chai,Waffle Testing when calling a getter with Contract.connect(Signer) - mocha.js

While testing some code on hardhat, I found an unexplained exception that was being thrown. I couldn't find any solution both on the discord channels, as well as in the etherjs docs.
This is my test suite:
import {ethers} from "hardhat";
import {loadFixture} from "ethereum-waffle";
describe.only("Images", async function () {
let result;
it("Creates images", async function () {
const {Decentragram} = await loadFixture(fixture);
const [owner, address2] = await ethers.getSigners();
result = await Decentragram.connect(owner).uploadImage();
let test = await Decentragram.connect(owner).test("test");
console.log(test);
});
});
This is my contract:
pragma solidity 0.8.6;
contract Decentragram {
string public name = "Decentragram";
// Store Images
mapping(uint256 => Image) public images;
mapping(string => uint256) public test;
struct Image {
uint256 id;
string hashImg;
string description;
uint256 tipAmount;
address payable author;
}
// Create Images
function uploadImage() public {
images[1] = Image({
id: 1,
hashImg: "abcd",
description: "description",
tipAmount: 0,
author: payable(address(0x0))
});
}
function getImage(uint256 id) public view returns (Image memory) {
Image memory img = images[id];
return img;
}
// Tip Images
}
This is the error I'm getting when running "npx hardhat test":
Error: call revert exception (method="test(string)", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.4.0)
at Logger.makeError (node_modules\#ethersproject\logger\src.ts\index.ts:213:28)
at Logger.throwError (node_modules\#ethersproject\logger\src.ts\index.ts:225:20)
at Interface.decodeFunctionResult (node_modules\#ethersproject\abi\src.ts\interface.ts:425:23)
at Contract.<anonymous> (node_modules\#ethersproject\contracts\src.ts\index.ts:332:44)
at step (node_modules\#ethersproject\contracts\lib\index.js:48:23)
at Object.next (node_modules\#ethersproject\contracts\lib\index.js:29:53)
at fulfilled (node_modules\#ethersproject\contracts\lib\index.js:20:58)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at runNextTicks (internal/process/task_queues.js:64:3)
at listOnTimeout (internal/timers.js:526:9)
at processTimers (internal/timers.js:500:7)

In the test suite:
import {ethers} from "hardhat";
import {loadFixture} from "ethereum-waffle";
describe.only("Images", async function () {
let result;
it("Creates images", async function () {
const {Decentragram} = await loadFixture(fixture);
const [owner, address2] = await ethers.getSigners();
result = await Decentragram.connect(owner).uploadImage();
let test = await Decentragram.test("test"); <<<<< don't use .connect() in order to call any default getter generated by solidity. Can't tell for sure but perhaps is because it is not necessary. There's not msg.sender on the call? Who knows? If you do, i'd appreciate your explanation below.
console.log(test);
});
});
I finally found the explanation. Turns out when I deployed my contract I used this fixture on mocha-waffle:
Here, when the contract gets deployed it's already tied to a signer (owner).
async function fixture([owner]: Wallet[], provider: Provider) {
const _Decentragram: Contract = await deployContract(owner, DecentragramJSON);
const Decentragram: Contract = await _Decentragram.deployed();
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
Here we instead use a contract factory deploy method, which doesn't need a Signer by default.
const _Decentagram2Fac = await ethers.getContractFactory("Decentragram");
const _Decentagram2 = await _Decentagram2Fac.deploy();
const Decentragram2 = await _Decentagram2.deployed();
return {Decentragram, Decentragram2};
}

Related

Why is this contract call failing (rust-counter increment)?

I am attempting to call the increment in the counter contract here, which is deployed to my account on testnet, using the following script:
const nearAPI = require("near-api-js");
require("dotenv").config();
const {parseSeedPhrase} = require("near-seed-phrase");
async function call() {
const mneumonic = process.env.nearmneumonic0?.trim() || "";
const ACCOUNT_ID = process.env.nearacct0?.trim() || "";
const keyStores = nearAPI.keyStores;
const keyPair = nearAPI.KeyPair;
const connect = nearAPI.connect;
const keyStore = new keyStores.InMemoryKeyStore();
const PRIVATE_KEY = parseSeedPhrase(mneumonic);
const keyPair_ = keyPair.fromString(PRIVATE_KEY.secretKey);
await keyStore.setKey("testnet", ACCOUNT_ID, keyPair_);
const config = {
keyStore,
networkId: "testnet",
nodeUrl: "https://rpc.testnet.near.org",
};
const near = await connect(config);
const account = await near.account(ACCOUNT_ID);
const contract = new nearAPI.Contract(
account,
ACCOUNT_ID,
{
changeMethods: ["increment", "decrement", "reset"],
viewMethods: ["get_num"],
sender: account,
}
);
let response = await contract.increment(
{
args: {
//arg_name: "value"
},
gas: 300000000000000
}
);
console.log(response);
}
call();
However, the response thrown is al followed:
Failure [acct.testnet]: Error: Contract method is not found
...
ServerTransactionError: Contract method is not found
I have looked through some of the docs, which mention to add changeMethod/viewMethod, however it seems there is still errors thrown.
Any help much appreciated!

Transparent proxy tests - Error: missing revert data in call exception; Transaction reverted without a reason string;

I am trying to create a new token contract using the ERC20 Upgradeable contracts provided from Openzeppelin (OZ). I created the token contract and then made some basic unit tests. However, in one of the tests, I expect to see "TransparentUpgradeableProxy: admin cannot fallback to proxy target", when calling an implementation method from the Proxy admin. In one of my projects it works fine, but in another made from scratch - it does not. The error that I receive is "Error: missing revert data in call exception; Transaction reverted without a reason string..."
Here is the code for the one that is failing:
// the contract ---
contract TokenName is ERC20PresetMinterPauserUpgradeable {
/**
* #dev Used to prevent implementation manipulation
*/
constructor() initializer {}
/**
* #dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, `PAUSER_ROLE` to the owner
* specified in the owner param.
* #param owner is the owner of the contract after initialization
* See {ERC20-constructor}.
*/
function initialize(string memory name, string memory symbol, address owner) public initializer {
require(owner != address(0x0), "New owner cannot be 0");
__ERC20PresetMinterPauser_init(name, symbol);
__ERC20_init(name, symbol);
_setupRole(DEFAULT_ADMIN_ROLE, owner);
_setupRole(MINTER_ROLE, owner);
_setupRole(PAUSER_ROLE, owner);
revokeRole(PAUSER_ROLE, _msgSender());
revokeRole(MINTER_ROLE, _msgSender());
revokeRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override(ERC20PresetMinterPauserUpgradeable) {
ERC20PresetMinterPauserUpgradeable._beforeTokenTransfer(from, to, amount);
}
}
// token unit tests ---
describe.only("Upgradeable token tests", function () {
let accounts;
let deployer;
let owner;
let tokenInstance;
let tokenName = "Token Name";
let tokenSymbol = "symbol";
const tokenAmount = '10000000000000000000';
before(async function() {
accounts = await ethers.getSigners();
deployer = accounts[0];
owner = accounts[1];
user = accounts[2];
})
it("should throw error when calling the implementation contract with the proxy admin", async function() {
const tokenContract = await ethers.getContractFactory(tokenSymbol);
tokenInstance = await upgrades.deployProxy(tokenContract, [tokenName, tokenSymbol, owner.address], { initializer: 'initialize(string,string,address)', unsafeAllow: ['constructor'] });
await tokenInstance.deployed();
console.log("default admin addr: ", await upgrades.erc1967.getAdminAddress(tokenInstance.address));
console.log("token instance addr: ", tokenInstance.address);
await upgrades.admin.changeProxyAdmin(tokenInstance.address, owner.address);
console.log("changed admin addr: ", await upgrades.erc1967.getAdminAddress(tokenInstance.address));
expect(await upgrades.erc1967.getAdminAddress(tokenInstance.address)).to.equal(owner.address);
//console.log("tokenInstance", tokenInstance);
console.log("owner addr: ", owner.address);
console.log("deployer addr: ", deployer.address);
console.log("admin changed ---");
await expect(tokenInstance.connect(owner).name()).to.be.revertedWith('TransparentUpgradeableProxy: admin cannot fallback to proxy target');
});
})
There is no much need to add the code of the other project, because it is exactly the same, but I will do in order to have it as comparison.
// token contract ---
contract MarsCoin is ERC20PresetMinterPauserUpgradeable {
uint counter;
function increment() public {
counter += 1;
}
function getCounter() public view returns (uint) {
return counter;
}
/**
* #dev Used to prevent implementation manipulation
*/
constructor() initializer {}
/**
* #dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE`, `PAUSER_ROLE` to the owner
* specified in the owner param.
* #param owner is the owner of the contract after initialization
* See {ERC20-constructor}.
*/
function initialize(string memory name, string memory symbol, address owner) public initializer {
require(owner != address(0x0), "New owner cannot be 0");
counter = 0;
__ERC20PresetMinterPauser_init(name, symbol);
__ERC20_init(name, symbol);
_setupRole(DEFAULT_ADMIN_ROLE, owner);
_setupRole(MINTER_ROLE, owner);
_setupRole(PAUSER_ROLE, owner);
revokeRole(PAUSER_ROLE, _msgSender());
revokeRole(MINTER_ROLE, _msgSender());
revokeRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override(ERC20PresetMinterPauserUpgradeable) {
ERC20PresetMinterPauserUpgradeable._beforeTokenTransfer(from, to, amount);
}
}
// token unit tests ---
const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");
describe.only("Mars Coin tests", function () {
let accounts;
let deployer;
let owner;
let tokenInstance;
let tokenName = "Mars Coin";
let tokenSymbol = "MARS";
const tokenAmount = '10000000000000000000';
before(async function() {
accounts = await ethers.getSigners();
deployer = accounts[0];
owner = accounts[1];
user = accounts[2];
})
it("Should deploy MARS token with correct owner set", async function () {
const MarsCoin = await ethers.getContractFactory("MarsCoin");
tokenInstance = await upgrades.deployProxy(MarsCoin, [tokenName, tokenSymbol, owner.address], { initializer: 'initialize(string,string,address)', unsafeAllow: ['constructor'] });
await tokenInstance.deployed();
const adminRole = await tokenInstance.DEFAULT_ADMIN_ROLE();
const minterRole = await tokenInstance.MINTER_ROLE();
const pauserRole = await tokenInstance.PAUSER_ROLE();
expect(await tokenInstance.name()).to.equal(tokenName);
expect(await tokenInstance.symbol()).to.equal(tokenSymbol);
expect(await tokenInstance.hasRole(adminRole, deployer.address)).to.equal(false);
expect(await tokenInstance.hasRole(minterRole, deployer.address)).to.equal(false);
expect(await tokenInstance.hasRole(pauserRole, deployer.address)).to.equal(false);
expect(await tokenInstance.hasRole(adminRole, owner.address)).to.equal(true);
expect(await tokenInstance.hasRole(minterRole, owner.address)).to.equal(true);
expect(await tokenInstance.hasRole(pauserRole, owner.address)).to.equal(true);
});
it("Should mint tokens to user account", async function() {
const tokenInstanceWithOwner = tokenInstance.connect(owner);
await tokenInstanceWithOwner.mint(user.address, tokenAmount);
const accountBalance = (await tokenInstance.balanceOf(user.address)).toString();
expect(accountBalance).to.equal(tokenAmount)
})
it("Should try to call contract implementation contract with deployer", async function () {
const tokenInstanceWithDeployer = tokenInstance.connect(deployer);
expect(await tokenInstanceWithDeployer.name()).to.equal(tokenName)
})
it("Should change the MARS token proxy admin correctly", async function() {
await upgrades.admin.changeProxyAdmin(tokenInstance.address, owner.address);
expect(await upgrades.erc1967.getAdminAddress(tokenInstance.address)).to.equal(owner.address);
})
it.only("Should throw on trying to call contract implementation from new proxy admin owner", async function () {
const MarsCoin = await ethers.getContractFactory("MarsCoin");
tokenInstance = await upgrades.deployProxy(MarsCoin, [tokenName, tokenSymbol, owner.address], { initializer: 'initialize(string,string,address)', unsafeAllow: ['constructor'] });
await tokenInstance.deployed();
await upgrades.admin.changeProxyAdmin(tokenInstance.address, owner.address);
expect(await upgrades.erc1967.getAdminAddress(tokenInstance.address)).to.equal(owner.address);
await expect(tokenInstance.connect(owner).name()).to.be.revertedWith('TransparentUpgradeableProxy: admin cannot fallback to proxy target');
})
});
Keep in mind the .only test that I am running, thus, the rest can be skipped, but I just paste it to have the exact code.
Okay, I succeeded to fix this by just creating the project from scratch.
npm init -> then follow the basic instructions
npm install hardhat
npx hardhat init -> create an advanced sample project and follow the instructions
npm install - all the necessary packages that are missing (some of the existing ones were re-installed with older version, because it seems the problem comes from a newer version library)
Very interesting stuff.. previously I just tried to delete all the folders like .cache, node_module, the built folder, package-lock.json and then to hit the npm install, but without success. I could not understand what was exactly the issue.
However, these are the packages that I am using:
"devDependencies": {
"#nomiclabs/hardhat-ethers": "^2.0.2",
"#nomiclabs/hardhat-etherscan": "^3.0.3",
"#nomiclabs/hardhat-waffle": "^2.0.1",
"chai": "^4.3.4",
"dotenv": "^10.0.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.4.1",
"eslint-plugin-promise": "^5.2.0",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.4.5",
"hardhat": "^2.6.1",
"hardhat-gas-reporter": "^1.0.8",
"prettier": "^2.6.2",
"prettier-plugin-solidity": "^1.0.0-beta.13",
"solhint": "^3.3.7",
"solidity-coverage": "^0.7.20"
},
"dependencies": {
"#openzeppelin/contracts": "^4.3.0",
"#openzeppelin/contracts-upgradeable": "^4.3.2",
"#openzeppelin/hardhat-upgrades": "^1.10.0"
}

Truffle tests not working, recieving: VM Exception while processing transaction: out of gas

I'm trying to test my contract but I'm receiving this error:
Events
✔ deploys a factory and an event
✔ marks caller as the event manager
1) allows the user to buy a ticket and stores their details
2 passing (1s)
1 failing
1) Events
allows the user to buy a ticket and stores their details:
c: VM Exception while processing transaction: out of gas
at Function.c.fromResults (node_modules/ganache-cli/build/ganache-core.node.cli.js:4:192416)
at w.processBlock (node_modules/ganache-cli/build/ganache-core.node.cli.js:42:50915)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
However, when looking over my code I believe I have more than enough gas and perhaps this error has to do with something else.
Here is the code for my test:
const assert = require('assert');
const ganache = require('ganache-cli');
const Web3 = require('web3');
const web3 = new Web3(ganache.provider({ gasLimit: 10000000 }));
const compiledFactory = require("../ethereum/build/EventFactory.json");
const compiledCampaign = require("../ethereum/build/Event.json");
let accounts;
let factory;
let eventAddress;
let event;
beforeEach(async () => {
accounts = await web3.eth.getAccounts();
factory = await new web3.eth.Contract(JSON.parse(compiledFactory.interface))
.deploy({ data: compiledFactory.bytecode })
.send({ from: accounts[0], gas: "10000000" });
await factory.methods.createEvent("test event", 2, 1, "Elton John's world tour", "Faketown auditorium", 100).send({
from: accounts[0],
gas: "10000000",
});
[eventAddress] = await factory.methods.getDeployedEvents().call();
event = await new web3.eth.Contract(
JSON.parse(compiledCampaign.interface),
eventAddress
);
});
describe("Events", () => {
it("deploys a factory and an event", () => {
assert.ok(factory.options.address);
assert.ok(event.options.address);
});
it("marks caller as the event manager", async () => {
const manager = await event.methods.manager().call();
assert.equal(accounts[0], manager);
});
it("allows the user to buy a ticket and stores their details", async () => {
await event.methods.buyTicket('Louie').send({
value: '1',
from: accounts[1]
});
const isContributor = await event.methods.attendees(account[1]).call();
assert(isContributor);
});
});
As you can see, my first two tests pass. However the third does not work. I believe I am providing enough gas. Here is my contract so you can look at the functions:
pragma solidity ^0.4.17;
contract EventFactory {
address[] public deployedEvents;
function createEvent(string title, uint max, uint cost, string description, string location, uint date) public {
address newEvent = new Event(title, msg.sender, max, cost, description, location, date);
deployedEvents.push(newEvent);
}
function getDeployedEvents() public view returns (address[]) {
return deployedEvents;
}
}
contract Event {
struct Attendee {
bool valid;
string name;
uint ticketNumber;
bool confirmed;
uint amountPaid;
uint confirmationCode;
}
address public manager;
string public title;
uint public maxAttendees;
uint public ticketsSold;
mapping(address=>Attendee) public attendees;
uint public ticketCost;
string public eventDescription;
string public eventLocation;
uint public eventDate;
mapping(address=>bool) public blacklist;
modifier restricted() {
require(msg.sender == manager);
_;
}
function random(uint seed) private view returns (uint) {
uint tmp = uint(block.blockhash(block.number - 1));
return uint(keccak256(tmp,seed)) % 1000000000;
}
function Event(string eventTitle, address creator, uint max, uint cost, string description, string location, uint date) public {
title = eventTitle;
manager = creator;
maxAttendees = max;
ticketsSold = 0;
ticketCost = cost;
eventDescription = description;
eventLocation = location;
eventDate = date;
}
function buyTicket(string name) public payable {
require(blacklist[msg.sender] != true);
require(ticketsSold != maxAttendees);
require(attendees[msg.sender].valid == false);
require(msg.value == ticketCost);
Attendee memory attendee = Attendee({
valid: true,
name: name,
ticketNumber: (maxAttendees - ticketsSold),
confirmed: false,
amountPaid: ticketCost,
confirmationCode: 0
});
ticketsSold++;
attendees[msg.sender] = attendee;
}
function challengeAttendee(address attendee, uint seed) public restricted returns (uint) {
require(attendees[attendee].valid == true);
uint code = random(seed);
attendees[attendee].confirmationCode = code;
return code;
}
function confirmTicketOwnerShip(uint code) public {
require(attendees[msg.sender].confirmationCode != 0);
attendees[msg.sender].confirmed = attendees[msg.sender].confirmationCode == code;
}
function confirmResponse(address attendee) public restricted view returns (bool) {
return attendees[attendee].confirmed;
}
function addToBlackList(address blockedAddress) public restricted {
blacklist[blockedAddress] = true;
}
function getEventDetails() public view returns (string, uint, uint, uint, string, string, uint) {
return (
title,
maxAttendees,
ticketsSold,
ticketCost,
eventDescription,
eventLocation,
eventDate
);
}
}
Try replacing this test:
it("allows the user to buy a ticket and stores their details", async () => {
await event.methods.buyTicket('Louie').send({
value: '1',
from: accounts[1]
});
const isContributor = await event.methods.attendees(account[1]).call();
assert(isContributor);
});
with the following one:
it("allows the user to buy a ticket and stores their details", async () => {
await event.methods.buyTicket('Louie').send({
value: '1',
gasPrice: '1',
from: accounts[1]
});
const isContributor = await event.methods.attendees(account[1]).call();
assert(isContributor);
});
I've found the error, unfortunately, it's simply a typo. On the sixth line down I use account[1] not accounts[1]. Also I had to specify the gas as the default amount was not enough.
My code now looks like:
it("allows the user to buy a ticket and stores their details", async () => {
await event.methods.buyTicket("Louie").send({
from: accounts[1],
value: '1',
gas: '20000000'
});
const isContributor = await event.methods.attendees(accounts[1]).call();
assert(isContributor.valid);
});

Uninfinite async function execution in next js

I'm new to next js. And I have one user.js file inside of my pages directory in next.js. This is the source code:
// pages/user.js
function user(props) {
const [userList, setUserList] = useState([])
const [roleList, setRoleList] = useState([])
async function initialFetch() {
const userList = await fetch('GET', GET_ALL_USERS)
setUserList(userList)
const roleList = await fetch('GET', GET_ALL_ROLES)
setRoleList(roleList)
console.log('userList in async')
console.log(userList)
}
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
console.log('userList outside')
console.log(userList)
return (
<>
<TableUserManagement users={userList} roles={roleList}/>
</>
)
};
user.getInitialProps = async (ctx) => {
const userList = await fetch('GET', GET_ALL_USERS)
const roleList = await fetch('GET', GET_ALL_ROLES)
return {userList, roleList}
}
The problem is that above async initialFetch() function is always called uninfinitively :
So what am I doing wrong here? Thank you
Note: I have tried to use useEffect() but the looping still happening. This the code :
useEffect(
() => {
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
console.log('user list diliuar')
console.log(userList)
}
)
This issue is not related to Next.js but React itself. This is the code that cause unfinite calls:
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
Since after setting any state, your component re-renders and that part of code keeps running again, and the fetch cause setting state again,... that loops forever.
You should move you data-fetching logic in side componentDidMount or useEffect. Remember to provide the dependency array of useEffect. In this case, you may only need to fetch data only once so you should provide the empty dependency array.
useEffect(() => {
async function initialFetch() {
const userList = await fetch('GET', GET_ALL_USERS)
setUserList(userList)
const roleList = await fetch('GET', GET_ALL_ROLES)
setRoleList(roleList)
}
if (!props.userList.status) {
initialFetch()
} else {
setUserList(props.userList)
setRoleList(props.roleList)
}
}, []);
P/s: you should name you React component in PascalCase (ex: User)

Make sure a method to a function has been await'ed for in Jest

I have a function like this (the code is simplified to make this code more readable)
import Adapter from "adapter-package";
const adapter = new Adapter();
async powerOn(): Promise<MyClass> {
const myClass = new MyClass();
await adapter.powerOn();
return myClass;
}
as you can see I am using await on the call of adapter.powerOn(). Now I am writing unit tests for this using Jest.
it("can power on the adapter", async () => {
const spy = jest.spyOn(Adapter.prototype, "powerOn");
const myClass = await MyClass.powerOn();
expect(myClass).toBeInstanceOf(MyClass);
expect(spy).toHaveBeenAwaitedFor();
^^^^^^^^^^^^^^^^^^^^
spy.mockRestore();
}, 10000);
The test that I have underlined does not exist, but it is what I would like to test. Can I know if a method I have called has been waited for?
edit:
Will Jenkins pointed out that it is not clear what I am asking. Basically I want to make sure that the Promise returned by adapter.powerOn() has been resolved by the time my function has been resolved. Because I already had an issue where I accidentially had removed the await before adapter.powerOn().
async powerOn(): Promise {
const myClass = new MyClass();
adapter.powerOn();
return myClass;
}
So when I called my function with
await powerOn();
that function was resolved, but it did not await the call to adapter.powerOn() and I had to spend some time debugging. So I would like to make sure that adapter.powerOn() is resolved by the time await powerOn() completes.
How about something like this?
it("can power on the adapter", async () => {
let hasResolved = false;
const spy = jest.spyOn(Adapter.prototype, "powerOn");
const myClass = await MyClass.powerOn();
expect(myClass).toBeInstanceOf(MyClass);
let hasResolved = false;
//if it has been awaited then this should complete
spy.returnValues[0].then(()=>{
hasResolved=true;
})
expect(hasResolved).to.be.true;
spy.mockRestore();
}, 10000)
You could use setInmediate to wait for all pending promises:
const flushPromises = () => new Promise(setImmediate);
it("can power on the adapter", async () => {
const spy = jest.spyOn(Adapter.prototype, "powerOn");
await expect(MyClass.powerOn()).resolves.toBeInstanceOf(MyClass);
await expect(flushPromises()).resolves.toBeTruthy();
}, 10000);
This test first checks if powerOn resolves and returns an instance of MyClass and then checks if every pending promise has been resolved or rejected.
This is the solution I found:
import { inspect } from "util";
it("waits for the adapter to have been powered on", async () => {
const spy = jest
.spyOn(Adapter.prototype, "powerOn")
.mockImplementation(() => new Promise<void>(res => setTimeout(res, 0)));
await Sblendid.powerOn();
const spyPromise = spy.mock.results[0].value;
expect(inspect(spyPromise)).toBe("Promise { undefined }");
}, 10000);
the line
spy.mock.results[0].value
will get me the promise returned by my mock. Using util.inspect on a promise
expect(inspect(spyPromise)).toBe("Promise { undefined }");
will give me "Promise { undefined }" on a fulfilled promise (that is returning undefined) and would return "Promise { <pending> }" if it weren't fulfilled, that is how I test it has been resolved (fulfilled) when await Sblendid.powerOn(); completed.

Resources