For the first one, I think it's because my local Hardhat Network is not forking mainnet correctly because of a configuration error, putting the network block outside of module.exports, leaving the contract in its own blockchain without any other contract. Once I confirmed it was forking mainnet by the warning that says Hardhat Network would be in decreased performance if you fork a very recent block, router.WETH() worked again.
For the second one, Hardhat actually tells me it's calling a non-existent account with no contract bytecode data. pairFor() calculates the pair address without looking up Uniswap V2 factory contract, and ends up producing invalid pair addresses inon Binance Smart Chain Testnet with PancakeSwap pairs. I alleviated it by replacing pairFor() with
import '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol';
IUniswapV2Factory factory = IUniswapV2Factory(factoryAddress);
factory.getPair(tokenA, tokenB);