Vue test utils: TypeError mounted - javascript

I am having an issue with Vue Test Utils. When I run a unit test, I am always confronted with:
TypeError{line: 73983, sourceURL: 'http://localhost:9876/base/index.js?045b00affe888fcd6b346c4fe50eadd13d471383', stack: 'mounted#http://localhost:9876/base/index.js?045b00affe888fcd6b346c4fe50eadd13d471383:73983:30.....
This only happens when I have the mounted() function in the Vue component
Settings.vue
mounted() {
this.$refs.address.update(this.profile.address)
},
Settings.spec.js
it('calls updateUserInformation before mount', () => {
const spy = sinon.spy(Settings.methods, 'updateUserInformation')
shallow(Settings, { propsData })
Vue.nextTick().then(() => {
spy.should.have.calledOnce()
})
})
I am using Mocha & Chai with vue-test-utils. Does anyone know why this is happening?
Thank you in advance!
UPDATE
Settings.vue component HTML
<vue-google-autocomplete
ref="address"
id="map"
classname="input"
placeholder="Address"
v-on:placechanged="getAddressPlaceChanged"
v-on:inputChange="getAddressInputChange"
:country="['sg']"
>
</vue-google-autocomplete>
Settings.vue component Javascript
export default {
components: {
GoogleMaps,
AutoComplete,
VueGoogleAutocomplete,
Partner,
},
watch: {
// whenever school changes, this function will run
school() {
// Check if school value is an empty string or character is lesser than FIX_STR_LENGTH
if (this.school === '' || this.school.length < this.FIX_STR_LENGTH) {
this.removeMarker('school')
}
this.fetchSchools()
},
},
methods: {
async onSubmit() {
// Check if input fields are empty
if (this.address !== undefined && this.phoneNumber !== null && this.school !== '') {
const { placeResultData = {}, addressData = {} } = this.address
let isSuccessful = false
let tempLat = null
let tempLong = null
let tempAddress = null
// Check if address is an empty object
if (_.isEmpty(this.address)) {
const { latlong = {}, address = '' } = this.profile
const [lat, long] = latlong.coordinates
tempLat = lat
tempLong = long
tempAddress = address
} else {
// User changed address location
tempLat = addressData.latitude
tempLong = addressData.longitude
tempAddress = placeResultData.formatted_address
}
// Validate school address array
let tempSchoolAddress = []
if (this.selectedSchool !== null) {
tempSchoolAddress.push(this.selectedSchool.postal_code)
} else {
tempSchoolAddress = this.profile.schoolAddress
}
// Construct user object for registration/update
const user = new User(
this.profile.name,
tempAddress,
tempLat,
tempLong,
tempSchoolAddress,
)
// If user does not exist in database, perform a POST API registration request
if (this.userExist === false) {
// Add user properties for user registration
user.phoneNumber = this.phoneNumber
await UserSession.register(user, localStorage.getItem('id_token')).then((response) => {
const { data = {} } = response
const profile = data.user
this.updateUserInformation(profile)
isSuccessful = true
this.profile = profile
}).catch((error) => {
console.log(error.response)
})
}
// Perform a PUT API update request
await UserSession.update(user, localStorage.getItem('id_token')).then((response) => {
const { data = {} } = response
const profile = data.user
this.updateUserInformation(profile)
isSuccessful = true
this.profile = profile
}).catch((error) => {
console.log(error.response)
})
if (isSuccessful) {
this.profileChanged()
this.hasChanged = true
}
}
},
profileChanged() {
this.$emit('profileChanged', this.profile)
},
addMarker(name, params) {
if (params === null || params === '') {
return
}
gMapSession.default(params).then((response) => {
const { location = {} } = response.data.results[0].geometry
// Remove existing marker before replacing it
this.removeMarker(name)
this.markers.push({
position: location,
name,
})
this.zoom = 11
}).catch((error) => {
console.log(error.response)
})
},
removeMarker(name) {
let index = 0
let exist = false
for (let i = 0; i < this.markers.length; i++) {
if (this.markers[i].name === name) {
index = i
exist = true
break
}
}
if (exist) {
this.markers.splice(index, 1)
}
},
// Function called when user selects an option from the school autocomplete dropdown
getSelectedSchoolData(event) {
this.selectedSchool = event
// Check if selected school is defined
if (this.selectedSchool !== undefined && this.selectedSchool !== null) {
this.addMarker('school', this.selectedSchool.postal_code)
} else {
this.removeMarker('school')
}
},
// Function called when user types in the address autocomplete input field
getAddressInputChange(data) {
const { newVal = {} } = data
if (newVal === '' || newVal.length < this.FIX_STR_LENGTH) {
this.removeMarker('user')
}
},
// Function called when user selects an option from the address autocomplete dropdown
getAddressPlaceChanged(addressData, placeResultData) {
this.address = {
placeResultData,
addressData,
}
if (addressData !== undefined && addressData !== null) {
this.addMarker('user', addressData.postal_code)
} else {
this.removeMarker('user')
}
},
async updateUserInformation(profile) {
this.phoneNumber = profile.phoneNumber
this.addMarker('user', profile.address)
// TODO: schoolAddress is an array and UI must cater to multiple schools
await SchoolSession.default(profile.schoolAddress[0]).then(async (res) => {
const { result = {} } = res.data
const { records = [] } = result
// Assume that a single postal code contains only 1 school
this.school = records[0].school_name
this.addMarker('school', records[0].postal_code)
})
},
// Fetch school information base on school search query
fetchSchools: _.debounce(function getSchools() {
if (this.school.trim() === '') {
this.schools = []
return
}
const vm = this
SchoolSession.default(this.school).then((response) => {
// JSON responses are automatically parsed.
const { records = {} } = response.data.result
vm.schools = records
}).catch((error) => {
console.log(error.response)
})
}, 500),
},
data() {
return {
FIX_STR_LENGTH: 5,
school: '',
address: '',
schools: [],
markers: [],
phoneNumber: null,
selectedSchool: null,
userExist: false,
hasChanged: false,
center: { lat: 1.3521, lng: 103.8198 },
zoom: 7,
}
},
async created() {
this.profile = this.$attrs
// Check if user was a registered member by phone number
if (this.profile.phoneNumber === undefined) {
return
}
// User exist in the database
this.userExist = !this.userExist
// Update form information
this.updateUserInformation(this.profile)
},
mounted() {
this.$refs.address.update(this.profile.address)
},
}

Related

NoSuchMethodError: The method '[]' was called on null. I/flutter (23049): Receiver: null I/flutter (23049): Tried calling: []("conversationID")

I am trying to implement messages feature on my application with firebase firestore using flutter/dart language but I am having the following error.
NoSuchMethodError: The method '[]' was called on null.
I/flutter (23049): Receiver: null
I/flutter (23049): Tried calling: [] ("conversationID")
Firestore function:
"use strict";
Object.defineProperty(exports, "__esModule",{value: true});
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.onConversationCreated =
functions.firestore.document("Conversations/{conversationID}")
.onCreate((snapshot, context) => {
let data = snapshot.data();
let conversationID = context.params.conversationID;
if (data) {
let members = data.members;
for (let index = 0; index < members.length; index++) {
let currentUserID = members[index];
let remainingUserIDs = members.filter((u) => u !== currentUserID);
remainingUserIDs.forEach((m) => {
return admin.firestore().collection("Users").doc(m).get().then((_doc) => {
let userData = _doc.data();
if (userData) {
return admin.firestore().collection("Users").doc(currentUserID).collection("Conversations").doc(m).create({
"conversationID": conversationID,
"image": userData.image,
"name": userData.name,
"unseenCount": 0,
});
}
return null;
}).catch(() => { return null; });
});
}
}
return null;
});
Flutter DatabaseService Class:
Future<void> createOrGetConversartion(String _currentID, String _recepientID,
Future<void> _onSuccess(String _conversationID)) async {
var _ref = _db.collection(_conversationsCollection);
var _userConversationRef = _db
.collection(_userCollection)
.doc(_currentID)
.collection(_conversationsCollection);
try {
var conversation =
await _userConversationRef.doc(_recepientID).get();
if (conversation.data != null) {
return _onSuccess(conversation.data()["conversationID"]);
} else {
var _conversationRef = _ref.doc();
await _conversationRef.set(
{
"members": [_currentID, _recepientID],
"ownerID": _currentID,
'messages': [],
},
);
return _onSuccess(_conversationRef.id);
}
} catch (e) {
print(e);
}
}
Your null check seems incorrect here.
if (conversation.data != null) {
return _onSuccess(conversation.data()["conversationID"]);
}
You are checking for conversation.data being null or not but using conversation.data() after null check. This only checks for the method is existing or not.
Instead of this, you should be doing something like this.
final data = conversation.data();
if (data != null) {
return _onSuccess(data["conversationID"]);
}

Trying to access state in oncompleted method

I have API query and getting the result and setting those in a state variable in Oncompleted method of API query, Now i am updating the same state variable in another api query "onCompleted method.
I am not able to access the result from state what i have set before in first api query and below is my code
Query 1:
const designHubQueryOnCompleted = designHubProject => {
if (designHubProject) {
const {
name,
spaceTypes
} = designHubProject;
updateState(draft => { // setting state here
draft.projectName = name;
draft.spaceTypes = (spaceTypes || []).map(po => {
const obj = getTargetObject(po);
return {
id: po.id,
name: obj.name,
category: obj.librarySpaceTypeCategory?.name,
description: obj.description,
warning: null // trying to modify this variable result in another query
};
});
});
}
};
const { projectDataLoading, projectDataError } = useProjectDataQuery(
projectNumber,
DESIGNHUB_PROJECT_SPACE_TYPES_MIN,
({ designHubProjects }) => designHubQueryOnCompleted(designHubProjects[0])
);
Query 2:
const {
// data: designhubProjectSpaceTypeWarnings,
loading: designhubProjectSpaceTypeWarningsLoading,
error: designhubProjectSpaceTypeWarningsError
} = useQuery(DESIGNHUB_PROJECT_LINKED_SPACETYPE_WARNINGS, {
variables: {
where: {
projectNumber: { eq: projectNumber }
}
},
onCompleted: data => {
const projectSpaceTypeWarnings = data.designHubProjectLinkedSpaceTypeWarnings[0];
const warnings = projectSpaceTypeWarnings.spaceTypeWarnings.reduce((acc, item) => {
const spaceTypeIdWithWarningState = {
spaceTypeId: item.spaceTypeProjectObjectId,
isInWarningState: item.isInWarningState
};
acc.push(spaceTypeIdWithWarningState);
return acc;
}, []);
console.log(state.spaceTypes); // trying to access the state here but getting empty array
if (state.spaceTypes.length > 0) {
const updatedSpaceTypes = state.spaceTypes;
updatedSpaceTypes.forEach(item => {
const spaceTypeWarning = { ...item };
spaceTypeWarning.warning = warnings?.filter(
w => w.spaceTypeId === spaceTypeWarning.id
).isInWarningState;
return spaceTypeWarning;
});
updateState(draft => {
draft.spaceTypes = updatedSpaceTypes;
});
}
}
});
Could any one please let me know where I am doing wrong with above code Or any other approach to modify the state, Many thanks in advance!!

Export firing before the function return, how can I fix this?

I have this function get the token from api and check if I have admin permission. The problem is my export its firing before of the checking of the function.
const async = [
{ path: '*', redirect: '/404', hidden: true }
]
var hasAdmin = getAdmin()
if (hasAdmin === undefined || hasAdmin === null) {
var token = null
var url = null
var uri = window.location.href.split('?')
if (uri.length === 2) {
var vars = uri[1].split('&')
var getVars = {}
var tmp = ''
vars.forEach(function(v) {
tmp = v.split('=')
if (tmp.length === 2) {
getVars[tmp[0]] = tmp[1]
}
token = getVars.AUTH_ID
url = getVars.DOMAIN
})
getUserAdmin(url, token)
.then(response => {
var hasAdmin = response.result
if (hasAdmin === true) {
console.log('hasAdmin: ' + hasAdmin)
async.push(adminRouter)
}
})
.catch(error => {
console.log(error)
})
}
} else if (hasAdmin === true) {
async.push(adminRouter)
}
export const asyncRoutes = async
console.log('hasAdmin2: ' + hasAdmin)
Here is how Im using the const asyncRoutes:
import { asyncRoutes, constantRoutes } from '#/router'
/**
* Use meta.role to determine if the current user has permission
* #param roles
* #param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* #param routes asyncRoutes
* #param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
I always need reload(f5) the page in first access to gets the menu admin.
Await from promise dont works because de export should be always in the top. Some idea how can I fix this?
obs: attention in variable hasAdmin

React Native Flatlist Not Rerendering Redux

My FlatList does not update when the props I pass from redux change. Every time I send a message I increase everyones unread message count in both firebase and in my redux store. I made sure to include key extractor and extra data, but neither helps. The only thing that changes the unread message count is a reload of the device. How do I make sure the flatList updates with MapStateToProps. I made sure to create a new object by using Object.Assign:
action:
export const sendMessage = (
message,
currentChannel,
channelType,
messageType
) => {
return dispatch => {
dispatch(chatMessageLoading());
const currentUserID = firebaseService.auth().currentUser.uid;
let createdAt = firebase.database.ServerValue.TIMESTAMP;
let chatMessage = {
text: message,
createdAt: createdAt,
userId: currentUserID,
messageType: messageType
};
FIREBASE_REF_MESSAGES.child(channelType)
.child(currentChannel)
.push(chatMessage, error => {
if (error) {
dispatch(chatMessageError(error.message));
} else {
dispatch(chatMessageSuccess());
}
});
const UNREAD_MESSAGES = FIREBASE_REF_UNREAD.child(channelType)
.child(currentChannel).child('users')
UNREAD_MESSAGES.once("value")
.then(snapshot => {
snapshot.forEach(user => {
let userKey = user.key;
// update unread messages count
if (userKey !== currentUserID) {
UNREAD_MESSAGES.child(userKey).transaction(function (unreadMessages) {
if (unreadMessages === null) {
dispatch(unreadMessageCount(currentChannel, 1))
return 1;
} else {
alert(unreadMessages)
dispatch(unreadMessageCount(currentChannel, unreadMessages + 1))
return unreadMessages + 1;
}
});
} else {
UNREAD_MESSAGES.child(userKey).transaction(function () {
dispatch(unreadMessageCount(currentChannel, 0))
return 0;
});
}
}
)
})
};
};
export const getUserPublicChannels = () => {
return (dispatch, state) => {
dispatch(loadPublicChannels());
let currentUserID = firebaseService.auth().currentUser.uid;
// get all mountains within distance specified
let mountainsInRange = state().session.mountainsInRange;
// get the user selected mountain
let selectedMountain = state().session.selectedMountain;
// see if the selected mountain is in range to add on additional channels
let currentMountain;
mountainsInRange
? (currentMountain =
mountainsInRange.filter(mountain => mountain.id === selectedMountain)
.length === 1
? true
: false)
: (currentMountain = false);
// mountain public channels (don't need to be within distance)
let currentMountainPublicChannelsRef = FIREBASE_REF_CHANNEL_INFO.child(
"Public"
)
.child(`${selectedMountain}`)
.child("Public");
// mountain private channels- only can see if within range
let currentMountainPrivateChannelsRef = FIREBASE_REF_CHANNEL_INFO.child(
"Public"
)
.child(`${selectedMountain}`)
.child("Private");
// get public channels
return currentMountainPublicChannelsRef
.orderByChild("key")
.once("value")
.then(snapshot => {
let publicChannelsToDownload = [];
snapshot.forEach(channelSnapshot => {
let channelId = channelSnapshot.key;
let channelInfo = channelSnapshot.val();
// add the channel ID to the download list
const UNREAD_MESSAGES = FIREBASE_REF_UNREAD.child("Public")
.child(channelId).child('users').child(currentUserID)
UNREAD_MESSAGES.on("value",snapshot => {
if (snapshot.val() === null) {
// get number of messages in thread if haven't opened
dispatch(unreadMessageCount(channelId, 0));
} else {
dispatch(unreadMessageCount(channelId, snapshot.val()));
}
}
)
publicChannelsToDownload.push({ id: channelId, info: channelInfo });
});
// flag whether you can check in or not
if (currentMountain) {
dispatch(checkInAvailable());
} else {
dispatch(checkInNotAvailable());
}
// if mountain exists then get private channels/ if in range
if (currentMountain) {
currentMountainPrivateChannelsRef
.orderByChild("key")
.on("value", snapshot => {
snapshot.forEach(channelSnapshot => {
let channelId = channelSnapshot.key;
let channelInfo = channelSnapshot.val();
const UNREAD_MESSAGES = FIREBASE_REF_UNREAD.child("Public")
.child(channelId).child('users').child(currentUserID)
UNREAD_MESSAGES.on("value",
snapshot => {
if (snapshot.val() === null) {
// get number of messages in thread if haven't opened
dispatch(unreadMessageCount(channelId, 0));
} else {
dispatch(unreadMessageCount(channelId, snapshot.val()));
}
}
)
publicChannelsToDownload.push({ id: channelId, info: channelInfo });
});
});
}
return publicChannelsToDownload;
})
.then(data => {
setTimeout(function () {
dispatch(loadPublicChannelsSuccess(data));
}, 150);
});
};
};
Reducer:
case types.UNREAD_MESSAGE_SUCCESS:
const um = Object.assign(state.unreadMessages, {[action.info]: action.unreadMessages});
return {
...state,
unreadMessages: um
};
Container- inside I hook up map state to props with the unread messages and pass to my component as props:
const mapStateToProps = state => {
return {
publicChannels: state.chat.publicChannels,
unreadMessages: state.chat.unreadMessages,
};
}
Component:
render() {
// rendering all public channels
const renderPublicChannels = ({ item, unreadMessages }) => {
return (
<ListItem
title={item.info.Name}
titleStyle={styles.title}
rightTitle={(this.props.unreadMessages || {} )[item.id] > 0 && `${(this.props.unreadMessages || {} )[item.id]}`}
rightTitleStyle={styles.rightTitle}
rightSubtitleStyle={styles.rightSubtitle}
rightSubtitle={(this.props.unreadMessages || {} )[item.id] > 0 && "unread"}
chevron={true}
bottomDivider={true}
id={item.Name}
containerStyle={styles.listItemStyle}
/>
);
};
return (
<View style={styles.channelList}>
<FlatList
data={this.props.publicChannels}
renderItem={renderPublicChannels}
keyExtractor={(item, index) => index.toString()}
extraData={[this.props.publicChannels, this.props.unreadMessages]}
removeClippedSubviews={false}
/>
</View>
);
}
}
Object.assign will merge everything into the first object provided as an argument, and return the same object. In redux, you need to create a new object reference, otherwise change is not guaranteed to be be picked up. Use this
const um = Object.assign({}, state.unreadMessages, {[action.info]: action.unreadMessages});
// or
const um = {...state.unreadMessages, [action.info]: action.unreadMessages }
Object.assign() does not return a new object. Due to which in the reducer unreadMessages is pointing to the same object and the component is not getting rerendered.
Use this in your reducer
const um = Object.assign({}, state.unreadMessages, {[action.info]: action.unreadMessages});

Firebase database how to list

I am trying to query a Firebase database. I have the following code, but it returns an empty list, when there is matching data.
loadData() {
this.firelist = this.af.database.list('/chat/', {
query: {
orderByChild: 'negativtimestamp'
}
}).map(items => {
const filtered = items.filter(item => {
item.memberId1 === this.me.uid;
});
return filtered;
});
// if me not in firelist then create new chat
if (this.me && this.me.uid) {
let chatFound: boolean = false;
console.log('this.firelist', this.firelist);
this.firelist.forEach(chatItems => {
console.log('chatItems', chatItems);
for (let i = 0; i < chatItems.length; i++) {
console.log('chatItems['+i+']', chatItems[i]);
let memberId1: string = chatItems[i].memberId1;
let memberId2: string = chatItems[i].memberId2;
if (memberId1 === this.me.uid || memberId2 === this.me.uid) {
chatFound = true;
break;
}
}
if (!chatFound) {
//this.createChat();
}
});
}
}
I think my problem is with the filter.
The following code creates a chat successfully:
createChat(img1: string, img2: string) {
this.af.database.list('/chat/').push({
memberId1: this.me.uid,
memberId2: this.you.uid,
img1: img1,
img2: img2,
displayName1: this.me.displayName,
displayName2: this.you.displayName,
lastMsg_text: 'todo',
timestamp: Date.now(),
negativtimestamp: -Date.now()
})
}
The following filter works:
this.firelist = this.af.database.list('/chat/', {
query: {
orderByChild: 'negativtimestamp'
}
}).map(items => {
const filtered = items.filter(
item => item.memberId1 === this.me.uid
);
return filtered;
});

Categories

Resources