Vue and Gnosis Safe: Cannot read properties of undefined (reading 'addListeners') - vue.js

I cought error, when I tried to add listeners. Can someone help me to solve this problem? I used Vue3 and Gnosis Safe.
mounted() {
const appsSdk = new initSdk();
const onSafeInfo = (safeInfo) => {
this.data.safeInfo = safeInfo;
appsSdk.initContracts();
};
const onTransactionConfirmation = ({ requestId, safeTxHash }) => {
console.log(requestId, safeTxHash);
};
const onTransactionRejection = ({ requestId }) => {
console.log(requestId);
};
appsSdk.addListeners({
onSafeInfo,
onTransactionConfirmation,
onTransactionRejection,
});
},

Related

How to pass reactive data to Vue components using pinia store elements?

GamePage.vue
Destructured the pinia state elements and action method
const $store = useGameStore();
const {game, teamOne, teamTwo} = storeToRefs($store);
const { getGame } = $store;
Passed the destructed variables to components
<player-stat-table
:title="teamTwo.name"
:players="teamTwo.players"
:teamColor="teamTwo.team_color"
/>
Table Display
store/game_store.js
I am trying to edit data from the above table using updatePlayer action, after successfully completing the action I am updating the entire store data by recalling the get action method. But the data in the table is not updating reactively, it's updating after page reload. How to update it reactively?
import { api } from 'boot/axios'
import { defineStore } from 'pinia'
import { splitPlayers } from 'src/helpers'
export const useGameStore = defineStore('game', {
state: () => ({
game: null,
teamOne: null,
teamTwo: null,
}),
getters: {
getTeamOne: state => state.teamOne,
getTeamTwo: state => state.teamTwo,
getGameData: state => state.game,
},
actions: {
getGame(payload) {
return new Promise((resolve, reject) => {
api.get(`/games/${payload.gameID}/`)
.then(resp => {
const data = resp.data;
const teams = splitPlayers(data)
this.game = data
this.teamOne = teams[0]
this.teamTwo = teams[1]
resolve(data)
})
})
},
updatePlayer(payload) {
return new Promise((resolve, reject) => {
api.put(`/playerstat/${payload.id}/`, data)
.then(resp => {
const data = resp.data;
this.getGame({gameID: data.game})
resolve(data)
})
})
},
}
})
First, you can get rid of you getters, cause due to pinia documentation,
as getters you can think of as the computed properties
and you're not computing anything. So you can simply access the state properties, what you are already doing in your GamePage.vue file.
Secondly, you should also consider async/await pattern instead of Promiste.then(). Like mentioned in the comments, there's a problem with promise constructor antipattern in the OP.
I also prefer writing my pinia stores with the setup() approach, because I think it fits the vue3/composition-api approach a bit better.
import { api } from 'boot/axios'
import { defineStore } from 'pinia'
import { splitPlayers } from 'src/helpers'
export const useGameStore = defineStore('game', () => {
const game = ref(null);
const teamOne = ref(null);
const teamTwo = ref(null);
const getGame = async (gameId) => {
const resp = await api.get(`/games/${gameId}/`);
const teams = splitPlayers(resp.data)
game.value = resp.data
teamOne.value = teams[0]
teamTwo.value = teams[1]
};
const updatePlayer = async (data) => {
const resp = await api.put(`/playerstat/${data.id}/`, data)
const gameId = resp.data.game;
await getGame(gameId)
};
return {
game,
teamOne,
teamTwo,
getGame,
updatePlayer
}
});

Testing custom hook - not wrapped in act warning

I' trying to test a custom hook but I receive this warning message
console.error node_modules/#testing-library/react-hooks/lib/core/console.js:19
Warning: An update to TestComponent inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.
This is my custom hook
import { useState, useEffect } from 'react'
import io from 'socket.io-client'
import config from './../../../../config'
const useNotificationsSocket = (user) => {
const [socket, setSocket] = useState(null)
const [numUnreadMessages, setNumUnreadMessages] = useState(0)
const configureSocket = socket => {
socket.on('connect', () => {
const data = {
user: user,
}
socket.emit('user joined', data)
})
socket && socket.on('messages updated', (data) => {
//console.log(data)
setNumUnreadMessages(data.numUnreadMessages)
})
}
useEffect(() => {
const fetchSocket = async () => {
const s = await io(config.nSocket.url, {transports: ['websocket']})
configureSocket(s)
setSocket(s)
}
// Check that user is not an empty object as this causes a crash.
user && user.Id && fetchSocket()
}, [user])
return [socket, numUnreadMessages]
}
export { useNotificationsSocket }
and this is the test
import { renderHook, act } from '#testing-library/react-hooks'
import { useNotificationsSocket } from './../hooks/useNotificationsSocket'
jest.mock('socket.io-client')
describe('useNotificationsSocket', () => {
it('returns a socket and numUnreadMessages', async () => {
const user = { Id: '1' }
const { result } = renderHook(() => useNotificationsSocket(user))
expect(result).not.toBeNull()
})
})
I've tried importing act and wrapping the code in a call to act but however I try to wrap the code I still get a warning and can't figure out how I should use act in this case.
Your hook is asynchronous, so you need to await its response:
describe('useNotificationsSocket', () => {
it('returns a socket and numUnreadMessages', async () => {
const user = { Id: '1' }
const { result } = renderHook(() => useNotificationsSocket(user))
await waitFor(() => expect(result).not.toBeNull())
})
})
Additionally, if you define multiple tests, you may encounter your original error if you fail to unmount the hook. At least this appears to be the behaviour in #testing-library/react v13.3.0. You can solve this by unmounting the hook when your test completes:
describe('useNotificationsSocket', () => {
it('returns a socket and numUnreadMessages', async () => {
const user = { Id: '1' }
const { result, unmount } = renderHook(() => useNotificationsSocket(user))
await waitFor(() => expect(result).not.toBeNull())
unmount()
})
})

Why am I getting "redux-persist/createPersistoid: error serializing state"?

I have no idea why I'm getting this error. there's no file referenced no line referenced. I only know the action thats executed which results in the error.
The full error:
console.error "redux-persist/createPersistoid: error serializing
state", TypeError: JSON.stringify cannot serialize cyclic structures.
Here is the action that causes the error:
export const loadInitialDiscover = () => {
return (dispatch) => {
dispatch({ type: types.DISCOVER_LOADING });
return getNewest(dispatch);
};
};
const getNewest = (dispatch) => {
return firebase
.firestore()
.collection('users')
.where('role', '==', 'a')
.where('active', '==', true)
.orderBy('createdAt')
.limit(10)
.get()
.then((querySnapshot) => {
const newest = [];
querySnapshot.forEach((queryDocSnapshot) => {
const profile = queryDocSnapshot.data();
newest.push(profile);
});
dispatch({ type: types.LOAD_NEWEST, payload: newest });
})
.catch((err) => loadFail(dispatch, err.message));
};
implementation of action in screen:
componentDidMount () {
//console.log('INITIAL REDUX STATE: ' + JSON.stringify(this.props.newest));
this.props.newest == null ? this.props.loadInitialDiscover() : null;
}
notice this .stringify() is commented out
Ive removed literally any and every existing JSON.stringify, deleted build folder, restarted bundler, restarted simulator. the error persists.
here is relevant reducer:
const INITIAL_STATE = {
newest: null,
loading: false,
error: ''
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.DISCOVER_LOADING:
return {
...state,
loading: true,
error: ''
};
case types.LOAD_FAIL:
return {
...state,
loading: false,
error: action.info
};
case types.LOAD_NEWEST:
return {
...state,
loading: false,
error: '',
newest: action.payload
};
}
}
and here's the persist config:
const persistAuthConfig = {
key: 'auth',
storage: AsyncStorage
};
const persistUserConfig = {
key: 'user',
storage: AsyncStorage
};
const persistDiscoverConfig = {
key: 'discover',
storage: AsyncStorage
};
const AppReducer = combineReducers({
auth: persistReducer(persistAuthConfig, AuthReducer),
user: persistReducer(persistUserConfig, UserReducer),
discover: persistReducer(persistDiscoverConfig, DiscoverReducer)
});
export default AppReducer;
How do I get this error gone?
The Firestore query response contains cyclic data (ref parameter) that is not possible to serialised by JSON.stringify hence the error from Redux-Persist, (yes; redux-persist use it under the hood). Just remove the ref parameter containing the cyclic data from all the objects inserted into the array newest array.
Also, do a check on other parameters that cannot be serialised by JSON.stringify like ref.
The below may help you to achieve this:-
const newest = []; querySnapshot.forEach((queryDocSnapshot) => {
const profile = queryDocSnapshot.data();
const {ref, ...profileSerialisable} = profile;
newest.push(profileSerialisable);
});
Please refer here for more discussions based on this.

useMutation not mutating the local state

I'm getting this error while trying to mutate the local state in apollo.
errInvariant Violation: Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql
Initial state
registration: {
__typename: 'Registration',
tempMerchantId: '',
authorizeProfile: {
__typename: 'AuthorizePersonProfile',
nid_front: '',
nid_back: '',
authorized_person_photo: ''
}
}
My mutation
export const setAuthorizePersonQuery = gql`
mutation setAuthorizePersonProfileInfo($authorizePerosnData: Object!){
setAuthorizePersonProfileInfo(authorizePersonData: $authorizePerosnData) #client
}
`;
My resolver
export const setAuthorizePersonProfileInfo = (
_, { authorizePersonData }, { cache }
) => {
try {
const prevData = cache.readQuery({ getAuthorizePersonProfileQuery });
cache.writeQuery({
getAuthorizePersonProfileQuery,
data: {
registration: {
__typename: 'Registration',
authorizeProfile: {
__typename: 'AuthorizePersonProfile',
...prevData.registration.authorizeProfile,
...authorizePersonData
}
}
}
});
} catch (e) {
console.log(`err${e}`);
}
return null;
};
I'm trying to mutate the local state on button press, the function is
const handlePressedNext = () => {
Promise.all([
setAuthorizePersonProfileInfo({
variables: { authorizePersonData: generateNidData() }
})
])
.then(() => {
navigation.navigate('Photograph');
});
};
generateNidData function is like bellow
const generateNidData = () => ({
nid_front: nidFrontImage,
nid_back: nidBackImage
});
I'm new to apollo client. I can not understand what I'm doing wrong. Can anyone help me figure out the problem?
getAuthorizePersonProfileQuery is not a valid option for readQuery. Presumably, you meant use query instead.

How to mock window.location.href with Jest + Vuejs?

Currently, I am implementing unit tests for my project and there is a file that contains window.location.href.
I want to mock this to test and here is my sample code:
it("method A should work correctly", () => {
const url = "http://dummy.com";
Object.defineProperty(window.location, "href", {
value: url,
writable: true
});
const data = {
id: "123",
name: null
};
window.location.href = url;
wrapper.vm.methodA(data);
expect(window.location.href).toEqual(url);
});
But I get this error:
TypeError: Cannot redefine property: href
at Function.defineProperty (<anonymous>)
How should I resolve it?
You can try:
global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, 'location', {
value: {
href: url
}
});
expect(window.location.href).toEqual(url);
Have a look at the Jest Issue for that problem:
Jest Issue
2020 Update
Basic
The URL object has a lot of the same functionality as the Location object. In other words, it includes properties such as pathname, search, hostname, etc. So for most cases, you can do the following:
delete window.location
window.location = new URL('https://www.example.com')
Advanced
You can also mock Location methods that you might need, which don't exist on the URL interface:
const location = new URL('https://www.example.com')
location.assign = jest.fn()
location.replace = jest.fn()
location.reload = jest.fn()
delete window.location
window.location = location
I have resolved this issue by adding writable: true and move it to beforeEach
Here is my sample code:
global.window = Object.create(window);
const url = "http://dummy.com";
Object.defineProperty(window, "location", {
value: {
href: url
},
writable: true
});
Solution for 2019 from GitHub:
delete global.window.location;
global.window = Object.create(window);
global.window.location = {
port: '123',
protocol: 'http:',
hostname: 'localhost',
};
The best is probably to create a new URL instance, so that it parses your string like location.href does, and so it updates all the properties of location like .hash, .search, .protocol etc.
it("method A should work correctly", () => {
const url = "http://dummy.com/";
Object.defineProperty(window, "location", {
value: new URL(url)
} );
window.location.href = url;
expect(window.location.href).toEqual(url);
window.location.href += "#bar"
expect(window.location.hash).toEqual("#bar");
});
https://repl.it/repls/VoluminousHauntingFunctions
Many of the examples provided doesn't mock the properties of the original Location object.
What I do is just replace Location object (window.location) by URL, because URL contains the same properties as Location object like "href", "search", "hash", "host".
Setters and Getters also work exactly like the Location object.
Example:
const realLocation = window.location;
describe('My test', () => {
afterEach(() => {
window.location = realLocation;
});
test('My test func', () => {
// #ts-ignore
delete window.location;
// #ts-ignore
window.location = new URL('http://google.com');
console.log(window.location.href);
// ...
});
});
Working example with #testing-library/react in 2020 for window.location.assign:
afterEach(cleanup)
beforeEach(() => {
Object.defineProperty(window, 'location', {
writable: true,
value: { assign: jest.fn() }
})
})
Extending #jabacchetta's solution to avoid this setting bleeding into other tests:
describe("Example", () => {
let location;
beforeEach(() => {
const url = "https://example.com";
location = window.location;
const mockLocation = new URL(url);
mockLocation.replace = jest.fn();
delete window.location;
window.location = mockLocation;
});
afterEach(() => {
window.location = location;
});
});
How to reassign window.location in your code base; the simplest working setup we found for our Jest tests:
const realLocation = window.location;
beforeEach(() => {
delete window.location;
});
afterEach(() => {
window.location = realLocation;
});
you can try jest-location-mock.
npm install --save-dev jest-location-mock
update jest configs at jest.config.js file or jest prop inside package.json:
setupFilesAfterEnv: [ "./config/jest-setup.js" ]
create jest-setup.js
import "jest-location-mock";
usage:
it("should call assign with a relative url", () => {
window.location.assign("/relative-url");
expect(window.location).not.toBeAt("/");
expect(window.location).toBeAt("/relative-url");
});
You can try a helper:
const setURL = url => global.jsdom.reconfigure({url});
describe('Test current location', () => {
test('with GET parameter', () => {
setURL('https://test.com?foo=bar');
// ...your test here
});
});
This is valid for Jest + TypeScript + Next.js (in case you use useRoute().push
const oldWindowLocation = window.location;
beforeAll(() => {
delete window.location;
window.location = { ...oldWindowLocation, assign: jest.fn() };
});
afterAll(() => {
window.location = oldWindowLocation;
});
JSDOM Version
Another method, using JSDOM, which will provide window.location.href and all of the other properties of window.location, (e.g. window.location.search to get query string parameters).
import { JSDOM } from 'jsdom';
...
const { window } = new JSDOM('', {
url: 'https://localhost/?testParam=true'
});
delete global.window;
global.window = Object.create(window);
I could not find how to test that window.location.href has been set with correct value AND test that window.location.replace() has been called with right params, but I tried this and it seems perfect.
const mockWindowLocationReplace = jest.fn()
const mockWindowLocationHref = jest.fn()
const mockWindowLocation = {}
Object.defineProperties(mockWindowLocation, {
replace: {
value: mockWindowLocationReplace,
writable: false
},
href : {
set: mockWindowLocationHref
}
})
jest.spyOn(window, "location", "get").mockReturnValue(mockWindowLocation as Location)
describe("my test suite", () => {
// ...
expect(mockWindowLocationReplace).toHaveBeenCalledWith('foo')
expect(mockWindowLocationHref).toHaveBeenCalledWith('bar')
})
Can rewrite window.location by delete this global in every test.
delete global.window.location;
const href = 'http://localhost:3000';
global.window.location = { href };
Based on examples above and in other threads, here is a concrete example using jest that might help someone:
describe('Location tests', () => {
const originalLocation = window.location;
const mockWindowLocation = (newLocation) => {
delete window.location;
window.location = newLocation;
};
const setLocation = (path) =>
mockWindowLocation(
new URL(`https://example.com${path}`)
);
afterEach(() => {
// Restore window.location to not destroy other tests
mockWindowLocation(originalLocation);
});
it('should mock window.location successfully', () => {
setLocation('/private-path');
expect(window.location.href).toEqual(
`https://example.com/private-path`
);
});
});
Probably irrelevant. But for those seeking a solution for window.open('url', attribute) I applied this, with help of some comments above:
window = Object.create(window);
const url = 'https://www.9gag.com';
Object.defineProperty(window, 'open', { value: url });
expect(window.open).toEqual(url);
Here's a simple one you can use in a beforeEach or ala carte per test.
It utilizes the Javascript window.history and its pushState method to manipulate the URL.
window.history.pushState({}, 'Enter Page Title Here', '/test-page.html?query=value');
I use the following way using the Jest's mocking mechanism (jest.spyOn()) instead of directly overwriting the object property.
describe("...", () => {
beforeEach(() => {
const originalLocation = window.location;
jest.spyOn(window, "location", "get").mockImplementation(() => ({
...originalLocation,
href: "http://dummy.com", // Mock window.location.href here.
}))
});
afterEach(() => {
jest.restoreAllMocks()
});
it("...", () => {
// ...
})
});
I learned it from this post.