Stack Exchange
Expected an event but reverts with reason `ERC721: caller is not token owner or approved`
JavaScript test code:
const { deployments, ethers, network } = require("hardhat")
const { assert, expect } = require("chai")
const { developmentChains } = require("../../helper-hardhat-config")

!developmentChains.includes(network.name)
  ? describe.skip
  : describe("NFTMarketplace test", function () {
      let nftMarketplaceContract,
        basicNftContract,
        nftMarketplace,
        basicNft,
        deployer,
        player
      const PRICE = ethers.utils.parseEther("1")
      const TOKEN_ID = ethers.BigNumber.from("0")
      beforeEach(async function () {
        accounts = await ethers.getSigners()
        deployer = accounts[0]
        player = accounts[1]
        await deployments.fixture(["all"])
        nftMarketplaceContract = await ethers.getContract("NftMarketplace")
        nftMarketplace = await nftMarketplaceContract.connect(deployer)
        basicNftContract = await ethers.getContract("BasicNft")
        basicNft = await basicNftContract.connect(deployer)
        await basicNft.mintNft()
        await basicNft.approve(basicNftContract.address, TOKEN_ID)
      })
      describe("BuyItem function", function () {
        it("Emits an event if nft is bought", async function () {
          await nftMarketplace.listItem(basicNft.address, TOKEN_ID, PRICE)
          nftMarketplace = nftMarketplaceContract.connect(player)
          expect(
            await nftMarketplace.buyItem(basicNft.address, TOKEN_ID, {
              value: PRICE,
            })
          ).to.emit("ItemBought")
        })
    })

Corresponding solidity function:
function buyItem(
    address nftAddress,
    uint256 tokenId
  ) external payable nonReentrant isListed(nftAddress, tokenId) {
    Listing memory listedItem = s_listings[nftAddress][tokenId];
    if (msg.value < listedItem.price) {
      revert NftMarketplace__SentLessThanListed(nftAddress, tokenId);
    }
    s_proceeds[listedItem.seller] += msg.value;
    delete s_listings[nftAddress][tokenId];
    IERC721(nftAddress).safeTransferFrom(
      listedItem.seller,
      msg.sender,
      tokenId
    );
    emit ItemBought(msg.sender, nftAddress, tokenId, listedItem.price);
  }

Error:
VM Exception while processing transaction: reverted with reason string 'ERC721: caller is not token owner or approved'
    at BasicNft.safeTransferFrom (@openzeppelin/contracts/token/ERC721/ERC721.sol:180)
    at BasicNft.safeTransferFrom (@openzeppelin/contracts/token/ERC721/ERC721.sol:168)
    at NftMarketplace.buyItem (contracts/NftMarketplace.sol:110)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at runNextTicks (node:internal/process/task_queues:65:3)
    at listOnTimeout (node:internal/timers:528:9)
    at processTimers (node:internal/timers:502:7)
    at HardhatNode._mineBlockWithPendingTxs (node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:1819:23)
    at HardhatNode.mineBlock (node_modules\hardhat\src\internal\hardhat-network\provider\node.ts:508:16)
    at EthModule._sendTransactionAndReturnHash (node_modules\hardhat\src\internal\hardhat-network\provider\modules\eth.ts:1522:18)

Some who know the mistake, help me with it.

This error happens because the owner of the token 0 must approve the NFT Marketplace and not the NFT contract.
Change this line:
await basicNft.approve(basicNftContract.address, TOKEN_ID)

to:
await basicNft.approve(nftMarketplaceContract.address, TOKEN_ID)

Additionally, ensure that:
Check the allowance for the token.
Make sure the token owner is the seller.

The problem is at that line:
IERC721(nftAddress).safeTransferFrom(
      listedItem.seller,
      msg.sender,
      tokenId
    );

You call the marketplace contract which itself calls IERC721. The problem is, your marketplace contract itself is not the owner of the NFT and therefore not allowed to transfer it.
To fix this, you need to make this call from the owner of the NFT before listing the NFT:
IERC721(_contractAddress).approve(_marketplaceContractAddress, tokenId)

That way, you will allow the marketplace to manage that NFT when it needs to be transferred to new wallet.
