fetch doesn't work in jest, and return TypeError: Network request failed - fetch

I am trying to migrate from karma + PhantomJS to Jest + jsDom, but I got a problem. all fetch in UT failed in Jest. I am trying to figure out the reason. So I just write a simple UT like this
import fetch from 'isomorphic-fetch';
import $ from 'jquery';
describe('test', () => {
it('should fetch success....', (done) => {
return fetch('http://www.ebay.com/', { method: 'get' })
.then((res) => {
console.log(res);
done();
})
.catch(err => console.log(err));
})
it('should get success....', (done) => {
$.get('http://www.ebay.com/', (res) => {
console.log(res);
done();
}).fail((xhr, statusText, err) => {
console.log(statusText, err);
})
})
})
but still get the error like this
TypeError: Network request failed
at XMLHttpRequest.xhr.onerror (/Users/davidhe/work/activenet/git/aui/node_modules/whatwg-fetch/fetch.js:436:16)
at XMLHttpRequest.callback.(anonymous function) (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTa
rget-impl.js:289:32)
at invokeEventListeners (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdo
m/lib/jsdom/living/events/EventTarget-impl.js:219:27)
at invokeInlineListeners (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:166:7)
at EventTargetImpl._dispatch (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:122:7)
at EventTargetImpl.dispatchEvent (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87
:17)
at XMLHttpRequest.dispatchEvent (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:61:35
)
at dispatchError (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:994:9)
at validCORSHeaders (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:1009:7)
at receiveResponse (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:871:12)
at Request.client.on.res (/Users/davidhe/work/activenet/git/aui/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:691:38)
at emitOne (events.js:96:13)
at Request.emit (events.js:188:7)
at Request.onRequestResponse (/Users/davidhe/work/activenet/git/aui/node_modules/request/request.js:1074:10)
at emitOne (events.js:96:13)
at ClientRequest.emit (events.js:188:7)
at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:473:21)
at HTTPParser.parserOnHeadersComplete (_http_common.js:99:23)
at Socket.socketOnData (_http_client.js:362:20)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at Socket.Readable.push (_stream_readable.js:134:10)
at TCP.onread (net.js:547:20)
this is my package.json
{
devDependencies:{
"jsdom": "^11.0.0",
"babel-jest": "^20.0.3",
"jest": "^20.0.4"
},
"dependencies": {
"isomorphic-fetch": "^2.2.1"
}
"jest": {
"browser": true,
"testEnvironment": "jsdom",
"cacheDirectory": "./node_modules/.cache",
"verbose": true,
"globals": {
"__STATIC__": true,
"__DEV__": false,
"__TESTING__": true
},
"transformIgnorePatterns": [
"/node_modules/(?!react-aaui).+\\.js$"
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/test/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
"^imports": "<rootDir>/test/__mocks__/jestImportsMock.js"
},
"testRegex": "(fetch\\.(test|spec))\\.(jsx|js)$",
"moduleFileExtensions": [
"json",
"jsx",
"js"
],
"moduleDirectories": [
"node_modules",
"src",
"test"
],
"modulePathIgnorePatterns": [],
"snapshotSerializers": [
"enzyme-to-json/serializer"
],
"collectCoverage": false,
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!**/__mocks__/**"
],
"coverageReporters": [
"text",
"html"
],
"coverageDirectory": "test/coverage",
"setupFiles": [
"./test/tests.initialState.jest.js"
],
"testPathIgnorePatterns": [
"/node_modules/",
"/examples/",
"/dist/"
]
}
}
Anyone can help me on it.

I encounter the same issue. And I use fetch-mock to solve it nicely.
import fetchMock from 'fetch-mock'
it ("normally should return success", () => {
fetchMock.getOnce('*', {user: 'ron'})
expect(fetch('http://your.example.com').then(x=>x.json())
.resolves.toEqual({user: 'ron})
})

this question is resolved by add
import { XMLHttpRequest } from 'xmlhttprequest';
global.XMLHttpRequest = XMLHttpRequest;
in tests.initialState.jest.js.
before, I was using w3c-xmlhttprequest, and it doesn't work.

In my case adding the full Url, it didn't show the error.
const login = rest.post('http://localhost:3001/api/auth/login', (req, res, ctx) => {

Related

ReduxToolKit | CreateEntityAdaptor userSelectors.selectAll giving Cannot read properties of undefined (reading 'map')

Hi i am using ReduxToolKit CreateEntityAdaptor for crud, when i get all users from API by using userSelectors.selectAll, it gives "Cannot read properties of undefined (reading 'map')".
let me show my API response.
{
"data": [
{
"id": 16,
"name": "Admin",
"email": "admin#admin.com",
"assigned_roles": [
"Administrator"
],
"created_at": "2022-10-06T20:08:32.000000Z"
}
],
"links": {
"first": "http://laravel-api.test/api/users?page=1",
"last": "http://laravel-api.test/api/users?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"active": false
},
{
"url": "http://laravel-api.test/api/users?page=1",
"label": "1",
"active": true
},
{
"url": null,
"label": "Next »",
"active": false
}
],
"path": "http://laravel-api.test/api/users",
"per_page": 15,
"to": 1,
"total": 1
}
}
using AsyncThunk for getting data from API services/userService file
import { createAsyncThunk} from "#reduxjs/toolkit";
import axios from "axios";
import { API_URL, ACCESS_TOKEN } from "../constants";
export const fetchUsers = createAsyncThunk(
'user/fetchUsers',
async (page) => {
const data = await axios(API_URL+'/users?page='+page,
{ method:'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${ACCESS_TOKEN}`
},
});
return data.data;
}
)
features/userSlice.js
import { createSlice, createEntityAdapter } from "#reduxjs/toolkit";
import { HTTP_STATUS } from "../constants";
import { fetchUsers } from "../services/userService";
const userAdapter = createEntityAdapter({
selectId: (user) => user.id,
});
const userSlice = createSlice({
name: "user",
initialState: {
loading: false,
status: null,
message: "",
},
reducers:{
pageByNumber: (state,{payload}) => {
state.page = payload.page
},
nextPage: (state, {payload}) => {
state.page = state.page++
},
previousPage: (state, {payload}) => {
state.page = state.page--
},
clear: (state) => {
state.status = null
state.message = null
}
},
extraReducers: {
[fetchUsers.pending]: (state, action) => {
state.loading = true
state.status = HTTP_STATUS.PENDING
},
[fetchUsers.fulfilled]: (state, { payload }) => {
console.log(payload.data);
state.loading = false
state.page = payload.meta.current_page
state.total_pages = Math.ceil(payload.meta.total/payload.meta.per_page)
userAdapter.setAll(state, payload.data)
state.status = HTTP_STATUS.FULFILLED
},
[fetchUsers.rejected]: (state, { payload }) => {
state.loading = false
state.status = HTTP_STATUS.REJECTED
},
},
});
export const userSelectors = userAdapter.getSelectors(
(state) => state.user
)
export const {pageByNumber, nextPage, previousPage,clear} = userSlice.actions
export default userSlice.reducer
views/users/index.js
i am not getting understand why there is map array error.
Hi i got the answer actually i had not passed my initial state to create Entity Adapter, below i have shown latest code for initial state.
initialState: userAdapter.getInitialState({ //Initial state should be wrapped in Adapter
loading: false,
status: null,
message: "",
}),

vite config proxy config don't rewrite path

I have a cors issue in my development with vue3 & vite, so I create a proxy config in my vite.config.js
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'#': resolve(__dirname, 'src'),
},
},
server: {
proxy: {
"/api": {
target: "https://###.com",
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/api/, ""),
},
}
}
})
when I use in my app my method:
createPayment() {
const url = "/api/webhook/###";
let formData = new FormData();
formData.append("firstName", this.first_name);
formData.append("lastName", this.last_name);
formData.append("email", this.email);
formData.append("phone", this.phone);
formData.append("cardNumber", this.c_number);
formData.append("cardExpiration", this.c_EXP);
formData.append("cardCCV", this.c_CVC);
const request = new Request(url, {
method: "POST",
body: formData,
headers: {
accept: 'application/json',
contentType: "application/json;charset=UTF-8",
AccessControlAllowOrigin: '*'
},
});
fetch(request)
.then(result => console.log(result))
.catch(error => console.log('error', error));
},
The Post all ways send from localhost, I can't understand why?
I make the config for change '/api' to 'https://###.com' ?
My Log:
Response {type: 'basic', url: 'http://localhost:3000/api/webhook/####', redirected: false, status: 404, ok: false, …}

Getting a fetch error using redux toolkit and RTK-Query

I am using RTK-Query, and Redux-toolkit for this app, and I created an api-slice with createApi, as per the docs.
When I run a request to the backend, I get a "FETCH_ERROR"; however, when I run the same request using Axios, I get the data correctly from the backend, which leads me to believe I have an error in my code. I am just not sure where exactly it is.
Here is the error:
Object {
"api": Object {
"config": Object {
"focused": true,
"keepUnusedDataFor": 60,
"middlewareRegistered": true,
"online": true,
"reducerPath": "api",
"refetchOnFocus": false,
"refetchOnMountOrArgChange": false,
"refetchOnReconnect": false,
},
"mutations": Object {},
"provided": Object {},
"queries": Object {
"test(undefined)": Object {
"endpointName": "test",
"error": Object {
"error": "TypeError: Network request failed",
"status": "FETCH_ERROR",
},
"requestId": "BWOuLpOxoDKTzlUYFLW4x",
"startedTimeStamp": 1643667104869,
"status": "rejected",
},
},
"subscriptions": Object {
"test(undefined)": Object {
"QJSCV641RznGWyudGWuMb": Object {
"pollingInterval": 0,
"refetchOnFocus": undefined,
"refetchOnReconnect": undefined,
},
},
},
},
"test": Object {
"data": Array [],
},
}
Here is the test slice:
import { createSlice } from "#reduxjs/toolkit";
const testSlice = createSlice({
name: "test",
initialState: {
data: [],
},
reducers: {
getData: (state) => {
state;
},
},
});
export const { getData } = testSlice.actions;
export default testSlice.reducer;
Here is the apiSlice:
import { createApi, fetchBaseQuery } from "#reduxjs/toolkit/query/react";
export const apiSice = createApi({
reducerPath: "test",
baseQuery: fetchBaseQuery({ baseUrl: process.env.REACT_APP_backend_url }),
endpoints: (builder) => ({
test: builder.query({
query: () => "/test",
}),
}),
});
export const { useTestQuery } = apiSice;
I solved it by changing the backend URL to my current ipv4 (for expo development, otherwise just your whatever your backend URL is) address in my .env file, then deleting cache, and restarting my app. In my case I was using expo so, expo r -c, and it worked.

Browsersync keeps reloading

I have Gulp + Browsersync working with Wordpress.
Everything was working well, reloading every time I change files.
But since yesterday only, Browsersync keeps reloading without any reason.
I didn't made any changes on my gulpfile. I tried reverting to a former commit from 2 days ago, same thing. I don't know where it comes from.
Here's my repo.
I tried :
downgrading browsersync to 2.24.4 from this post
adding this to gulp :
socket: {
clients: {
heartbeatTimeout: 60000
}
},
Here are my files.
gulpfile.babel.js
import { src, dest, watch, series, parallel } from 'gulp';
import yargs from 'yargs';
import sass from 'gulp-sass';
import cleanCss from 'gulp-clean-css';
import gulpif from 'gulp-if';
import postcss from 'gulp-postcss';
import sourcemaps from 'gulp-sourcemaps';
import autoprefixer from 'autoprefixer';
import imagemin from 'gulp-imagemin';
import del from 'del';
import webpack from 'webpack-stream';
import named from 'vinyl-named';
import browserSync from 'browser-sync';
import zip from 'gulp-zip';
import info from './package.json';
import replace from 'gulp-replace';
import wpPot from 'gulp-wp-pot';
import tailwindcss from 'tailwindcss';
// import purgeCss from 'gulp-purgecss';
const PRODUCTION = yargs.argv.prod;
export const clean = () => del(['dist']);
export const styles = () => {
return (
src('src/scss/app.scss')
.pipe(gulpif(!PRODUCTION, sourcemaps.init()))
.pipe(sass().on('error', sass.logError))
.pipe(postcss([tailwindcss('./tailwind.config.js')]))
.pipe(gulpif(PRODUCTION, postcss([autoprefixer])))
.pipe(gulpif(PRODUCTION, cleanCss({ compatibility: 'ie8' })))
// .pipe(
// gulpif(
// PRODUCTION,
// purgeCss({
// content: ['**/*.php']
// })
// )
// )
.pipe(gulpif(!PRODUCTION, sourcemaps.write()))
.pipe(dest('dist/css'))
.pipe(server.stream())
);
};
export const images = () => {
return src('src/images/**/*.{jpg,jpeg,png,svg,gif}')
.pipe(gulpif(PRODUCTION, imagemin()))
.pipe(dest('dist/images'));
};
export const copy = () => {
return src(['src/**/*', '!src/{images,js,scss}', '!src/{images,js,scss}/**/*']).pipe(
dest('dist')
);
};
export const watchForChanges = () => {
watch('src/scss/**/*.scss', styles);
watch('src/images/**/*.{jpg,jpeg,png,svg,gif}', series(images, reload));
watch(['src/**/*', '!src/{images,js,scss}', '!src/{images,js,scss}/**/*'], series(copy, reload));
watch('src/js/**/*.js', series(scripts, reload));
watch('**/*.php', reload);
};
export const scripts = () => {
return src(['src/js/bundle.js'])
.pipe(named())
.pipe(
webpack({
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
},
mode: PRODUCTION ? 'production' : 'development',
devtool: !PRODUCTION ? 'inline-source-map' : false,
output: {
filename: '[name].js'
}
})
)
.pipe(dest('dist/js'));
};
/***** Generating a POT file *****/
export const pot = () => {
return src('**/*.php')
.pipe(
wpPot({
domain: 'boosters',
package: info.name
})
)
.pipe(dest(`languages/${info.name}.pot`));
};
/***** Compress theme into a ZIP file after renaming _themename *****/
export const compress = () => {
return src([
'**/*',
'!node_modules{,/**}',
'!bundled{,/**}',
'!src{,/**}',
'!.babelrc',
'!.gitignore',
'!gulpfile.babel.js',
'!package.json',
'!package-lock.json'
])
.pipe(
gulpif(
// prevent bug if there are zip files inside the theme
file => file.relative.split('.').pop() !== 'zip',
replace('boosters', info.name)
)
)
.pipe(zip(`${info.name}.zip`))
.pipe(dest('bundled'));
};
/****** BrowserSync ******/
const server = browserSync.create();
export const serve = done => {
server.init({
proxy: 'localhost:8888/boosters', // put your local website link here
snippetOptions: {
ignorePaths: 'boosters/wp-admin/**'
},
ghostMode: false
// socket: {
// clients: {
// heartbeatTimeout: 60000
// }
// },
// logLevel: 'debug',
// logFileChanges: true,
// logConnections: true
});
done();
};
export const reload = done => {
server.reload();
done();
};
/****** Series & Parallel Scripts ******/
export const dev = series(clean, parallel(styles, images, scripts, copy), serve, watchForChanges);
export const build = series(clean, parallel(styles, images, scripts, copy), pot, compress);
export default dev;
package.json
{
"name": "boosters",
"version": "1.0.0",
"description": "A Wordpress website for Boost.rs by DoubleCat Studio",
"main": "gulpfile.babel.js",
"scripts": {
"start": "gulp",
"build": "gulp build --prod"
},
"repository": {
"type": "git",
"url": "git+ssh://git#github.com/indaviande/boosters.git"
},
"author": "Vianney Bernet",
"license": "ISC",
"bugs": {
"url": "https://github.com/indaviande/boosters/issues"
},
"browserslist": [
"last 4 version",
"> 1%",
"ie 11"
],
"homepage": "https://github.com/indaviande/boosters#readme",
"devDependencies": {
"#babel/core": "^7.4.3",
"#babel/preset-env": "^7.4.3",
"#babel/register": "^7.4.0",
"autoprefixer": "^9.5.1",
"babel-loader": "^8.0.5",
"browser-sync": "^2.26.7",
"del": "^4.1.0",
"gulp": "^4.0.0",
"gulp-clean-css": "^4.0.0",
"gulp-if": "^2.0.2",
"gulp-imagemin": "^5.0.3",
"gulp-postcss": "^8.0.0",
"gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.2",
"gulp-sourcemaps": "^2.6.5",
"gulp-wp-pot": "^2.3.5",
"gulp-zip": "^4.2.0",
"tailwindcss": "^1.0.4",
"vinyl-named": "^1.1.0",
"webpack-stream": "^5.2.1",
"yargs": "^13.2.2"
},
"dependencies": {
"tar": ">4.4.7"
}
}
Browsersync has a log that you can inspect to see which files are being changed that trigger the reload. You can also increase specificity of what you're watching/ignoring. Start off small and gradually increase your glob paths until you can see reloads on all changes.
server.init({
logLevel: 'debug',
files: [
'wp-content/themes/**/*.css',
'wp-content/themes/**/*.js',
'wp-content/themes/**/*.php',
],
ignore: [
'folder-to-ignore/**/*.*'
]
});

Implementing Push notification using Strongloop

I am trying to use strongloop loopback sdk 2.0. I tried to use the following code https://github.com/strongloop/loopback-component-push/tree/master/example/server which is loopback version 1.7.0. But when i try compile with version 2.0, it throws me error
Error: The data in model-config.json is in the unsupported 1.x format.
I had also tried as per the strong loop tutorial, but still it does not work. Anyone has suggestion or sample code on how to implement PUSH notification using loopback 2.0?
Create 4 models application, installation, notification, push
In common/models/application.json
{
"name": "push",
"plural": "Push",
"base": "Model",
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
In common/models/installation.json
{
"name": "installation",
"base": "Installation",
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
In common/models/notification.js
{
"name": "notification",
"base": "Notification",
"properties": {},
"validations": [],
"relations": {},
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "sendMessage"
}
],
"methods": []
}
In common/models/push.json
{
"name": "push",
"plural": "Push",
"base": "Model",
"properties": {},
"validations": [],
"relations": {},
"acls": [],
"methods": []
}
server/datasource.json
...
...
"push": {
"name": "push",
"connector": "loopback-component-push",
"installation": "installation",
"notification": "notification",
"application": "application"
}
In common/models/notification.js
module.exports = function(Notification) {
var badge = 1;
//DEFINING A PROPERTY IN NOTIFICATION FOR SENDING PUSH MESSAGE
Notification.sendMessage = function(message, registrationId, from, callback) {
var app = this.app;
from = from? from:'COMPANY_NAME';
sendMessage(app, message, registrationId, from, callback);
}//sendMessage Notification method..
//FUNCTION FOR SENDING PUSH MESSAGE..
var sendMessage = function(app, message, registrationId, from, callback){
var Application = app.models.application;
var PushModel = app.models.push;
var note = new Notification({
expirationInterval: 3600, // Expires 1 hour from now.
badge: badge++,
// sound: 'ping.aiff',
message: message,
messageFrom: from
});
PushModel.notifyById(registrationId, note, function (err) {
if (err) {
console.error('Cannot notify %j: %s', registrationId, err.stack);
return callback(err);
}
console.log('Pushing notification to %j', registrationId);
callback(null, []);
});
}//sendMessage function
//Now registering the method
Notification.remoteMethod(
'sendMessage',
{
accepts: [{arg: 'message', type: 'string', required:true},
{arg: 'registrationId', type: 'string', required:true},
{arg: 'from', type: 'string', required:true}],
description: "Sending message from notification.."
}
)
}//module.exports
Now inside server/ folder create a file push-service.js
module.exports = function(app) {
var Notification = app.models.notification;
var Application = app.models.application;
var PushModel = app.models.push;
function startPushServer() {
PushModel.on('error', function(err) {
console.error('Push Notification error: ', err.stack);
});
// Pre-register an application that is ready to be used for testing.
// You should tweak config options in ./config.js
var loopbackApp = {
id: 'loopback-push-application',
userId: 'strongloop',
name: config.appName,
description: 'loopback Push Notification Service',
pushSettings: {
apns: {
certData: "APPLE CERT. DATA",
keyData: "APPLE KEY DATA",
pushOptions: {
// Extra options can go here for APN
},
feedbackOptions: {
batchFeedback: true,
interval: 300
}
},
gcm: {
serverApiKey: "PASTE YOUR GOOGLE GCM KEY HERE"
}
}
};
updateOrCreateApp(function(err, appModel) {
if (err) {
throw err;
}
console.log('Application id: %j', appModel.id);
});
function updateOrCreateApp(cb) {
Application.findOne({
where: {
id: loopbackApp.id
}
},
function(err, result) {
if (err) cb(err);
if (result) {
delete loopbackApp.id;
result.updateAttributes(loopbackApp, cb);
} else {
return registerApp(cb);
}
});
} //updateOrCreate function
Application.beforeSave = function(next) {
if (this.name === loopbackApp.name) {
this.id = 'loopback-push-application';
}
next();
};
Application.register(
loopbackApp.userId,
loopbackApp.name, {
description: loopbackApp.description,
pushSettings: loopbackApp.pushSettings
},
function(err, app) {
if (err) {
return cb(err);
}
return cb(null, app);
}
);
} //register App
} //startPushServer
startPushServer();
};
Now finally in server.js
....
....
//Adding push service to the backend..
require('./push-service')(app);
....
Now run the loopback server open api explorer and go to NOTIFICATION->SendMessage method and type any message and it will send push notification on connected devices.
NOTE: You also need to configure push services from android/iphone to enable sending push application. For detail check loopback documentation.
Please check out this example - https://github.com/strongloop/loopback-component-push/tree/master/example/server-2.0

Resources