Closed JackLazenbyZigzag closed 10 months ago
Thanks for reporting @JackLazenbyZigzag . Can you share your jest config, tsconfig, and package.json?
No problem at all @rattrayalex! We use Nx to manage our projects via a monorepo setup so I will share the jest and ts configs for the particular project that is failing.
Here they are:
package.json:
{
"dependencies": {
"@angular/animations": "^15.2.0",
"@angular/common": "^15.2.0",
"@angular/compiler": "^15.0.0",
"@angular/core": "^15.2.0",
"@angular/fire": "^7.6.0",
"@angular/forms": "^15.2.0",
"@angular/platform-browser": "^15.2.0",
"@angular/platform-browser-dynamic": "^15.2.0",
"@angular/router": "^15.2.0",
"@awesome-cordova-plugins/adjust": "^6.4.0",
"@awesome-cordova-plugins/core": "^6.4.0",
"@awesome-cordova-plugins/mixpanel": "^6.4.0",
"@awesome-cordova-plugins/purchases": "^6.4.0",
"@awesome-cordova-plugins/social-sharing": "^6.4.0",
"@awesome-cordova-plugins/status-bar": "^6.4.0",
"@azure/identity": "^3.3.0",
"@azure/keyvault-secrets": "^4.7.0",
"@azure/service-bus": "^7.9.0",
"@capacitor/app": "^5.0.6",
"@capacitor/camera": "^5.0.6",
"@capacitor/core": "^5.0.6",
"@capacitor/device": "^5.0.6",
"@capacitor/haptics": "^5.0.6",
"@capacitor/network": "^5.0.6",
"@capacitor/preferences": "^5.0.6",
"@contentful/rich-text-html-renderer": "^16.1.1",
"@ionic/angular": "^6.7.0",
"@microsoft/applicationinsights-web": "^3.0.2",
"@nestjs/axios": "^0.0.8",
"@nestjs/common": "^9.4.0",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.2.0",
"@nestjs/cqrs": "^9.0.3",
"@nestjs/event-emitter": "^1.4.1",
"@nestjs/jwt": "^10.0.3",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.4.0",
"@nestjs/schedule": "^2.2.1",
"@nestjs/typeorm": "^9.0.1",
"@nestjs/websockets": "^9.4.0",
"@ngneat/until-destroy": "^10.0.0",
"@ngrx/effects": "13.2.0",
"@ngrx/eslint-plugin": "^16.2.0",
"@ngrx/router-store": "15.4.0",
"@ngrx/store": "13.2.0",
"@ngx-translate/core": "^14.0.0",
"app-root-path": "^3.1.0",
"applicationinsights": "^2.7.3",
"argon2": "^0.29.1",
"azure-devops-node-api": "^12.1.0",
"cache-manager": "^5.2.3",
"canvas-confetti": "^1.6.0",
"capacitor-native-settings": "^5.0.1",
"capacitor-rate-app": "^4.0.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"com.adjust.sdk": "^4.32.0",
"compass-mixins": "^0.12.12",
"configcat-js": "^8.1.1",
"contentful": "^9.3.5",
"csv-parse": "5.5.0",
"currency-symbol-map": "^5.1.0",
"es6-promise-plugin": "^4.2.2",
"express": "^4.17.3",
"firebase": "^9.23.0",
"firebase-admin": "11.10.1",
"json-rules-engine": "^6.4.2",
"jwks-rsa": "^2.1.3",
"minisearch": "^6.1.0",
"mixpanel-browser": "^2.47.0",
"mssql": "^10.0.0",
"ng-circle-progress": "^1.7.1",
"ngrx-forms": "^8.0.0",
"ngx-sse-client": "^3.0.0",
"node-ipinfo": "^3.4.2",
"onesignal-cordova-plugin": "^3.3.1",
"onesignal-node": "^3.4.0",
"openai": "^4.6.0",
"passport": "^0.6.0",
"passport-azure-ad": "^4.3.5",
"passport-headerapikey": "^1.2.2",
"passport-jwt": "^4.0.1",
"qs": "^6.11.2",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"seedrandom": "^3.0.5",
"shallow-equal-object": "^1.1.1",
"sqlite3": "^5.1.5",
"stream-chat": "^8.11.0",
"stream-chat-angular": "^4.35.0",
"svg-path-properties": "^1.2.0",
"swiper": "^8.4.7",
"tedious": "^16.4.0",
"ts-loader": "^9.4.4",
"tslib": "^2.6.2",
"typeorm": "^0.3.17",
"webpack": "5.88.2",
"yargs": "^17.3.0",
"zone.js": "0.13.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.2.0",
"@angular-eslint/eslint-plugin": "^15.2.0",
"@angular-eslint/eslint-plugin-template": "^15.2.0",
"@angular-eslint/template-parser": "^15.2.0",
"@angular/cli": "^15.2.0",
"@angular/compiler": "^15.2.0",
"@angular/compiler-cli": "^15.2.0",
"@angular/language-service": "^15.2.0",
"@azure/storage-blob": "^12.15.0",
"@capacitor/android": "^5.0.0",
"@capacitor/cli": "^5.0.0",
"@capacitor/ios": "^5.0.0",
"@commitlint/cli": "^17.7.1",
"@commitlint/config-conventional": "^17.7.0",
"@ionic/angular-toolkit": "^10.0.0",
"@nestjs/schematics": "^9.0.3",
"@nestjs/testing": "^9.2.0",
"@ngneat/spectator": "^14.0.0",
"@ngrx/schematics": "15.4.0",
"@ngrx/store-devtools": "15.4.0",
"@nrwl/cypress": "14.0.5",
"@nrwl/eslint-plugin-nx": "14.0.5",
"@nrwl/jest": "14.0.5",
"@nrwl/linter": "14.0.5",
"@nrwl/nest": "14.0.5",
"@nrwl/node": "14.0.5",
"@nrwl/nx-cloud": "14.0.3",
"@nrwl/workspace": "^14.0.5",
"@nxtend/capacitor": "13.0.0",
"@nxtend/ionic-angular": "13.1.0",
"@swc/cli": "^0.1.55",
"@swc/core": "^1.3.83",
"@swc/jest": "^0.2.29",
"@types/app-root-path": "^1.2.5",
"@types/bcryptjs": "^2.4.3",
"@types/canvas-confetti": "^1.6.1",
"@types/express": "^4.17.13",
"@types/jest": "27.4.1",
"@types/jest-when": "^3.5.2",
"@types/mixpanel-browser": "^2.47.1",
"@types/mssql": "^8.1.2",
"@types/node": "^20.6.0",
"@types/passport-azure-ad": "^4.0.8",
"@types/passport-http": "^0.3.8",
"@types/passport-jwt": "^3.0.9",
"@types/supertest": "^2.0.12",
"@types/uuid": "^9.0.3",
"@types/yargs": "^17.0.7",
"@typescript-eslint/eslint-plugin": "5.60.1",
"@typescript-eslint/parser": "5.62.0",
"axios": "^0.27.2",
"babel-jest": "29.6.4",
"contentful-cli": "^2.8.6",
"contentful-management": "^10.45.0",
"cypress": "^12.15.0",
"cypress-localstorage-commands": "^2.2.4",
"dotenv": "^16.3.1",
"eslint": "8.48.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-cypress": "^2.10.3",
"eslint-plugin-jest": "^26.0.0",
"husky": "^8.0.3",
"jasmine-marbles": "0.9.2",
"jest": "27.5.1",
"jest-extended": "^2.0.0",
"jest-mock-extended": "^3.0.4",
"jest-preset-angular": "13.1.1",
"jest-when": "^3.6.0",
"lint-staged": "^14.0.1",
"ng-mocks": "^14.11.0",
"node-loader": "^2.0.0",
"nodemon": "^3.0.1",
"nx": "^14.1.7",
"postcss": "^8.4.29",
"prettier": "2.8.8",
"supertest": "^6.3.3",
"ts-jest": "27.1.4",
"ts-node": "^10.7.0",
"typescript": "4.9.5"
}
}
jest.config.js:
module.exports = {
displayName: 'app-backend-user-e2e',
preset: '../../../../jest.preset.js',
globals: {
'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' },
},
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../../../coverage/libs/app/backend/user-e2e',
};
tsconfig:
{
"extends": "../../../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
Thanks!
Thanks! It looks like your jest and tsconfig extend base configs, would you mind sharing those as well?
(I believe this may be the same bug mentioned here: https://github.com/openai/openai-node/issues/243#issuecomment-1709123367)
@rattrayalex of course! As an FYI, I have obfsucated some things that are business specific to our project, in case it looks a bit empty! :-) We also only extend from a base tsconfig, the jest side of things just uses the rules out of the box that jest suggests.
base tsconfig:
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": ".",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"importHelpers": true,
"target": "es2015",
"typeRoots": ["node_modules/@types"],
"lib": ["es2019", "dom", "es2020.string"],
"paths": {
"@angular/*": ["./node_modules/@angular/*"],
},
"rootDir": ".",
"skipLibCheck": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noImplicitAny": true
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}
great, thanks @JackLazenbyZigzag ! We'll try to take a look at this on Monday.
(I believe this may be the same bug mentioned here: #243 (comment))
@rattrayalex I had a look at that prior to creating this and it does look like it could be similar!
Thank you for your help, appreciate it. Have a great rest of your weekend!
@JackLazenbyZigzag what jest testEnvironment
/@jest-environment
are you running your tests in, is it jsdom
or something else besides 'node'
?
We weren't able to reproduce the issue when using the node
environment, but we can reproduce using the jsdom
environment because jsdom still doesn't polyfill fetch
: https://github.com/jsdom/jsdom/issues/1724.
We were able to get chat completion requests working with the jsdom
environment by adding import 'cross-fetch/polyfill'
before any import from 'openai'
. But warning: we couldn't get any file upload methods to work that way. Let me know if this works for you in the short term, we're still investigating if there are better solutions to this.
Okay we were able to get even file uploads to work in the jsdom
environment by using
/**
* @jest-environment jsdom
*/
import 'formdata-polyfill'
import 'whatwg-fetch'
import OpenAI, { toFile } from 'openai';
in that specific order.
Hi @jedwards1211 - thanks for this! I've added the import for cross-fetch/polyfill, and that resolved it for our existing project test suite (so not adding anything new test-wise). However, when trying to test our service specifically that uses the library, it still doesn't like it. I'm getting the below from the import for formdata-polyfill:
TS7016: Could not find a declaration file for module formdata-polyfill .
/Users/jacklazenby/WebstormProjects/zigzag/node_modules/formdata-polyfill/formdata.min.js
implicitly has an any type.
and, if I ignore this and try to test, I get this:
Test suite failed to run
ReferenceError: FormData is not defined
at Object.<anonymous> (../../../../node_modules/openai/_shims/form-data.js:5:20)
at Object.<anonymous> (../../../../node_modules/openai/src/uploads.ts:66:15)`
This only happens when I test a file that is directly using the openai library though. If I don't do that, the test suite now runs correctly.
@JackLazenbyZigzag would you be able to use whatwg-fetch
? We weren't able to get file uploads to work with cross-fetch
. Also it seems I was confused and we didn't actually need formdata-polyfill
to do file uploads with whatwg-fetch
.
@jedwards1211 at this point I'm not trying to do any file related things. I've just built a very basic test suite for a basic service.ts file that handles the openai library interaction, like so:
service ts file:
import 'cross-fetch/polyfill';
import 'whatwg-fetch';
import { Injectable } from '@nestjs/common';
import { ChatCompletionMessageParam } from 'openai/resources/chat';
import OpenAI from 'openai';
import { setupMessage } from '../../../domain/openai/openai';
const openai = new OpenAI({
apiKey: 'someapikey',
});
@Injectable()
export class OpenAIService {
async getResponse(message: string, userId: string) {
const messageToSendChat: ChatCompletionMessageParam = {
content: message,
role: 'user',
};
const response = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [setupMessage, messageToSendChat],
user: userId,
});
return response.choices[0].message.content;
}
}
test ts file:
import {OpenAIService} from './openai.service';
import {Test, TestingModule} from '@nestjs/testing';
describe('OpenAIService', () => {
let service: OpenAIService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
OpenAIService,
],
}).compile();
service = module.get<OpenAIService>(OpenAIService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
And this gives me the error:
Test suite failed to run
ReferenceError: FormData is not defined
at Object.<anonymous> (../../../../node_modules/openai/_shims/form-data.js:5:20)
at Object.<anonymous> (../../../../node_modules/openai/src/uploads.ts:66:15)
Huh, in whatever version of the jest jsdom environment I have, FormData
is defined...I think we could rework our shims to where you only get an error like that if you try to do a file upload.
Btw you only need one of cross-fetch/polyfill
and whatwg-fetch
, they're both polyfills for the fetch
API.
@jedwards1211 I have removed whatwg-fetch for now, and removed the test file. I can re-add it once a fix or workaround has been identified :-)
@JackLazenbyZigzag did you install 'formdata-polyfill'
before importing it?
Have you tried using the node
jest environment instead of jsdom for your backend tests?
@rattrayalex yes, I have installed 'formdata-polyfill
before importing it.
Also, just re-checked our jest.config for this test suite and it was always using node. Apologies for the mix up!
If I change it to jsdom, then it does pass. Obviously this isn't ideal as it could increase test run speed, and we're running this in a backend environment which shouldn't require jsdom for our needs.
If you can create a repository that reproduces the issue it would be the most helpful, otherwise, to make sure we're on the same page, you're able to reproduce the fetch is not defined
error under all of the following conditions?
jest.config
has testEnvironment: 'node'
/**
* @jest-environment node
*/
(or doesn't have a @jest-environment
pragma)
Can you also share your jest.preset.js
and any related files?
I wonder if some jest plugin or config is affecting its module resolution.
@jedwards1211 I can get a test repo put together in the next day or so if needed, but in the meantime, in response to your questions regarding environment. In our case:
'node'
for all of our configs. (we have since changed one to jsdom to allow these tests to pass for openai, but when it fails it is when it is set to node)Jest.preset is pretty small, but here it is:
const nxPreset = require('@nrwl/jest/preset');
module.exports = {
...nxPreset,
testPathIgnorePatterns: ['<rootDir>/src/environments/environment.test.ts'],
};
jest.config.js for this library:
module.exports = {
displayName: 'app-backend-user',
preset: '../../../../jest.preset.js',
globals: {
'ts-jest': { tsconfig: '<rootDir>/tsconfig.spec.json' },
},
testEnvironment: 'jsdom',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../../../coverage/libs/app/backend/user',
};
Okay I was able to repro on Jest 27, I was testing on Jest 29 and didn't notice that was different from your project until now. I think support for package.json exports
must have been added to Jest in a later version. The solution we're working on is to have you import 'openai/shims/node'
in cases like this.
@dilizarov @JackLazenbyZigzag are you able to upgrade to Jest 29? If so, can you confirm the issue does not appear on that version?
@rattrayalex we have a lot of jest dependencies in our project that mean we can't upgrade to the latest version of Jest from 27 without a lot of extra work, which I can't quickly do to investigate this I'm afraid. I will see what I can do over the next few days, though.
Ah ok, don't worry about it then. We hope to have a workaround out within a day or two.
We got the same problem here in AWS Lambda NodeJS 16 runtime.
@ciryon can you share a minimal repo that reproduces the issue you're seeing?
(Note that Node 16 is now EOL and we'll be dropping support for it soon)
@rattrayalex not easily made into a minimal repo I'm afraid. The issue appeared when upgrading from version 3 of the library, and I've tried to rollback from latest all the way to 4.0.1 and the same problem still appears.
Gotcha. And you only experience the problem on Lambda, not locally?
Yes, it works locally!
I can also report that it works if I use the Node 18 runtime on AWS Lambda.
Ah, great. Thanks Christian! Glad you've found something that works – we'll be dropping support Node 16 support in the next release.
For me updating all jest libraries to the latest versions helped.
Hello there! There seems to have been a few issues around this that have been resolved recently, but I'm still getting it so thought I would share just in case it's something different.
Confirm this is a Node library issue and not an underlying OpenAI API issue
Describe the bug
When building and running the OpenAI library in our NestJS API, we can do so locally with no issue ; we can communicate with it and get responses etc. However, we have a Jest testing suite, and when trying to test, it fails with:
ReferenceError: fetch is not defined
at Object. (../../../../node_modules/openai/_shims/fetch.js:8:17)
at Object. (../../../../node_modules/openai/core.js:64:17)
at Object. (../../../../node_modules/openai/src/index.ts:116:7)
To Reproduce
Code snippets
No response
OS
macOS
Node version
Node v18.17.1
Library version
openai 4.6.0