Jest tests fail while regular code works.
Bug Report
Describe the bug
.quote() throws Error: No set-cookie header present in Yahoo's response. Something must have changed, please report. only in jest testing. Outside of jest it works as expected
Minimal Reproduction
import yahooFinance from "yahoo-finance2"
describe('getSharePrice', () => {
it('should fetch the quote', async () => {
const quote = await yahooFinance.quote("MSFT");
console.log('quote: ', quote);
});
});
Environment
Browser or Node: node
Node version (if applicable):
Npm version:
Browser verion (if applicable):
Library version (e.g. 1.10.1): 2.13.3
Additional Context
Hey @mikhail, interesting find. On face value all I can say here is that I guess jest is doing something to fetch that is affecting things. Could I also trouble you to check this with v3 too? As a lot of the underlying architecture has changed, and it's possible this might just work there.
I actually tried V3 briefly but couldn't figure out how to get it to work. It said the entire method was not found. I'll dig in a bit more and try a few things. If you have any hunches let me know
Hey, thanks for the quick reply.
Let me know your setup and the exact error, when you have a chance.
I just need to forewarn that I'm currently abroad with very limited time over the next month unfortuantely :/ But definitely would like to make sure v3 is useable in all supported environments.
In case it's related, there was an issue affecting some bundlers, like webpack/nextjs, that was just fixed in https://github.com/gadicc/node-yahoo-finance2/issues/928. But still not sure exactly which error you got.
Still can't upgrade to v3. Even the copy/paste example doesn't work:
const symbol = 'OCDO.L';
const queryOptions = { modules: ['price', 'summaryDetail'] }; // defaults
const result = await yahooFinance.quoteSummary(symbol, queryOptions);
error:
TS2769: No overload matches this call. The `this` context of type [huge block of code] is not assignable to method's this of type ModuleThis
Thanks, @mikhail. Can you just confirm your environment and node version, and if you saw UPGRADING.md? The rest of the docs aren't updated yet.
@gadicc upgrading worked -- thanks for the doc. However the jest (original bug) is still failing.
I'm on Windows, node v20.9. now using next-major
It's definitely jest + yahoo-finance2 conflict. Any invocation inside of jest causes No set-cookie header present in Yahoo's response. Something must have changed, please report.
Custom cookie manager fixes this:
import YahooFinance from "yahoo-finance2";
import fetchCookie from 'fetch-cookie';
import { CookieJar } from 'tough-cookie';
import { fetch as undiciFetch } from 'undici';
// Minimal fetch + cookie jar wiring (improves crumb reliability in Jest)
if (!(global as any).__YF_CUSTOM_FETCH_INSTALLED__) {
try {
const jar = new CookieJar();
(global as any).fetch = fetchCookie(undiciFetch as any, jar) as any;
(global as any).__YF_CUSTOM_FETCH_INSTALLED__ = true;
} catch (e) {
// eslint-disable-next-line no-console
console.warn('Custom fetch-cookie setup failed (continuing):', e);
}
}
const yf = new YahooFinance({ suppressNotices: ["yahooSurvey"] });
jest.setTimeout(30000);
describe('finance live smoke (minimal)', () => {
it('quote MSFT (no assertions)', async () => {
const quote: any = await yf.quote('MSFT');
// eslint-disable-next-line no-console
console.log('[MSFT quote keys]', Object.keys(quote).slice(0, 12));
});
});```
Hey @mikhail
Thanks for reporting back, and for posting your findings and workaround.
I wonder where the actual problem is though, it seems like jest is doing something to fetch?
For me it was enough to just:
global.fetch = undiciFetch
for it to work again, didn't need the custom cookie manager or other imports.
A few further clarifications...
The global.fetch = undiciFetch has to happen before calling new YahooFinance(), because we store a ref to it, i.e.
const YahooFinance = require("yahoo-finance2").default;
const undiciFetch = require('undici').fetch;
global.fetch = undiciFetch;
const yf = new YahooFinance({suppressNotices: ["yahooSurvey"] });
You can (currently) override that with the currently undocumented and not necessarily stable _env:
const yf = new YahooFinance({suppressNotices: ["yahooSurvey"] });
yf._env.fetch = undiciFetch;
This issue highlights the fact that maybe we shouldn't store a ref for compatibility reasons, but still not really sure what jest is doing here. Anyway, it's on my radar!
Good catch! I assumed it was cookies related because of the error message, but yeah it's something jest does with fetch. I couldn't find what jest might be up to. Quick search didn't reveal anything obvious
Yeah, also didn't find anything on a quick search :/
Since the new 3.6.0 published now, we at least don't store a ref anymore by default, which makes things easier to reason about, but unfortunately still doesn't solve this.
(I thought perhaps we were storing a ref, then jest was changing globalThis.fetch, and maybe we were using the wrong fetch after that... but even using globalThis.fetch at call time has the same error).
In any event, the new flexibility (you can override fetch in a few different places) will probably be useful for anyone writing interesting tests with mocks or otherwise want more control over which fetch is used.
But... leaving this issue open for now 😅