node-yahoo-finance2 icon indicating copy to clipboard operation
node-yahoo-finance2 copied to clipboard

Jest tests fail while regular code works.

Open mikhail opened this issue 6 months ago • 12 comments

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

mikhail avatar Jun 13 '25 21:06 mikhail

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.

gadicc avatar Jun 14 '25 04:06 gadicc

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

mikhail avatar Jun 14 '25 04:06 mikhail

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.

gadicc avatar Jun 14 '25 04:06 gadicc

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.

gadicc avatar Jul 01 '25 01:07 gadicc

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

mikhail avatar Aug 07 '25 18:08 mikhail

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 avatar Aug 08 '25 08:08 gadicc

@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.

mikhail avatar Aug 09 '25 18:08 mikhail

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));
  });
});```

mikhail avatar Aug 09 '25 19:08 mikhail

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.

gadicc avatar Aug 11 '25 08:08 gadicc

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!

gadicc avatar Aug 11 '25 13:08 gadicc

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

mikhail avatar Aug 11 '25 13:08 mikhail

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 😅

gadicc avatar Aug 11 '25 14:08 gadicc