Jasmine react component, test keeps failing - javascript

I'm trying to pass a jasmine test for my react component, I would like to test the select all checkbox unchecks when the publish event is fired from one of the checked checkboxs. As not all all of the checkboxes will then be checked, so the select all needs to uncheck. Below is my jasmine test and component, I'm new to testing react components anyhelp much appreciated:
Currently get error, undefined is not an object this.stubs.subscribe
var React = require('react/addons');
var postal = require('postal');
var contactChannel = postal.channel("contact")
var SelectAll = require('../../../components/controls/SelectAll');
var channelStub = require('../../stub/channelStub');
// When subscribe event check checkbox
describe("Select All Checkbox Specification", function() {
describe("Checking deselect response", function () {
it("On receive subscribe event uncheck the select all box", function () {
// when the select all is created it will create the channel
// publish an event to the channel (de-select event)
instance = TestUtils.renderIntoDocument(<SelectAll />);
// publish event to check all checkboxes
contactChannel.publish({
channel: 'contact',
topic: 'selectAll',
data: {
selectAll: true
}});
var checkbox = TestUtils.findRenderedDOMComponentWithTag(instance, "input");
// publish event to uncheck select all checkbox
contactChannel.publish({
channel: "basket",
topic: "deselectedContact",
data: {}
});
// Checking the module - to see if the checkbox inside is unchecked
var checkbox = TestUtils.findRenderedDOMComponentWithTag(instance, "input");
var data = this.stubbed.subscribe();
// check box checked should be false
expect(data.topic === 'deselectedContact');
expect(input.checked === false);
});
React component:
var postal = require('postal');var postal = require('postal');
var contactChannel = postal.channel("contact");
var React = require('react');
var SelectAll = React.createClass({
getInitialState: function() {
return {
checked:false
};
},
setUnChecked: function(){
this.setState({checked: false});
},
handler: function(e) {
var updatedContacts = [],
contacts = this.props.data.contacts,
topic = 'selectAll',
checked = false,
channel = 'contact';
contactChannel.publish({
channel: channel,
topic: topic,
data: {
selectAll: event.target.checked
}});
this.setState({checked: event.target.checked});
},
render: function() {
return (
<div className="contact-selector">
<input type="checkbox" checked={this.state.checked}
onChange={this.handler} ref="checkAll" />
</div>
);
},
componentDidMount: function() {
var self = this;
contactChannel.subscribe("deselectedContact", function(data) {
self.setUnChecked();
});
}
});
module.exports = SelectAll;
ChannelStub:
var lastPublished = {},
lastTopicSubscribed = '',
lastCallbackSubscribed;
var ChannelStub = {
publish: function(data) {
lastPublished = data;
},
getLastPublished: function() {
return lastPublished;
},
subscribe: function(topic, callback) {
lastCallbackSubscribed = callback;
lastTopicSubscribed = topic;
}
};
module.exports = ChannelStub;

Related

How to delete item from Firebase Database?

I have created a simple todo in react and I want to remove the selected task in todo. But when I run this code and try to delete any task, it gives an error:
firebase.child failed: First argument was an invalid path: "undefined"
componentWillMount: function() {
this.firebaseRef = new Firebase('https://simpletodosapp.firebaseio.com/todos');
var that = this;
this.firebaseRef.once('value', function(snapshot){
snapshot.forEach(function(data){
var todos = data.val();
todos['key'] = data.key;
that.setState({tasks: todos})
})
});
},
handleTodo: function(text) {
var newTask = this.state.tasks.concat(text)
this.firebaseRef.push(newTask)
this.setState({tasks: newTask})
},
handleDelete: function(task_id, key) {
this.firebaseRef.child(key).remove();
var remove = this.state.tasks.splice(task_id, 1);
this.setState({itasks: remove})
},

ReactJS + Fluxxor cascading actions causes error

I've got a parent component that has 2 child components;
UPDATE
I've rewritten some statements and code to make it more understandable.
Parent: ReservationFormComponent
Children: ReservationTypePanel & ReservationCalendarPanel
The parent component ReservationFormComponent initially displays the ReservationTypePanel only. The other sibling ReservationCalendarPanel is hidden until an item is selected on ReservationTypePanel.
So the problem is when an item is selected in ReservationTypePanel the ReservationCalendarPanel is rendered with initial values set in the ReservationFormStore store. Particularly
initialize: function(){
this.reservationType = void 8;
this.pickupTime = moment().add('minutes',30);
}
So when the ReservationCalendarPanel is rendered, its child Component DateTimeField which accepts the state pickupTime get re-rendered and fires up the onChange event which calls for another action
return DateTimeField({
pickupTime: pickupTime,
onChange: function(time){
// Here is where the action gets called again
this$.getFlux().actions.setReservationPickupTime(time);
}
});
And greets me with this error Uncaught Error: Cannot dispatch an action while another action is being dispatched
I've tried my best to trim down the codes below. I wasn't using JSX because the original code was in LiveScript so I just took the compiled code to display here instead.
This is the parent component ReservationFormComponent
ReservationFormComponent = React.createClass({
get flux(){ // Instantiating Fluxxor
return new Fluxxor.Flux({ // These are the stores
'reservation-form': new ReservationFormStore,
'reservation-types': new ReservationTypeStore
}, { // These are the actions
setReservationType: function(value){
return this.dispatch('SET_RESERVATION_TYPE', value);
},
setReservationPickupTime: function(value){
return this.dispatch('SET_RESERVATION_PICKUP_TIME', value);
}
});
},
componentWillMount: function(){
this.flux.store('reservation-form').addListener('change', this.onChange);
},
onChange: function(){ // This triggers the re-render to display the ReservationCalendarPanel
this.setState({
pickupTime: this.flux.store('reservation-form').pickupTime
});
},
render: function() {
reservationType = this.state.reservationType;
return form({
className: 'container'
}, ReservationTypePanel({
flux: this.flux
}), reservationType ? ReservationCalendarPanel({
flux: this.flux
}) : null // This conditional to mount or not mount the component
);
}
});
The ReservationTypePanel Component. Here, the rendered component listens to onClick event and dispatches setReservationType action.
ReservationTypePanel = React.createClass({
mixins: [fluxxor.FluxMixin(react)],
onSelectReservationType: function(reservationType){
var this$ = this;
return function(event){
this$.getFlux().actions.setReservationType(reservationType);
};
},
render: function() {
var this$ = this;
return ReservationTypeItem({
onClick: this$.onSelectReservationType(type);
})
}
});
The ReservationCalendarPanel Component. Here is where the DateTimeField is rendered and receives the state from the ReservationFormStore and sets the value which causes another dispatch. This is where the error comes.
ReservationCalendarPanel = React.createClass({
mixins: [fluxxor.FluxMixin(react)],
getInitialState: function() {
return {pickupTime: moment()} // sets the current time
},
componentWillMount: function(){
this.getFlux().store('reservation-form').addListener('change-pickup-time', this.onFlux);
},
componentWillUnmount: function(){
this.getFlux().store('reservation-form').removeListener('change-pickup-time', this.onFlux);
},
render: function() {
var this$ = this;
if (this.state.pickupTime) {
pickupTime = moment(this.state.pickupTime);
}
return DateTimeField({
date: pickupTime,
onChange: function(time){
// Here is where the action gets called again
this$.getFlux().actions.setReservationPickupTime(time);
}
});
});
This is the DateTimeField this is where the
DateTimeField = React.createClass({
getInitialState: function(){
return {
text: ''
};
},
componentWillReceiveProps: function(nextProps){
this.setDate(nextProps.date);
},
componentDidMount: function(){
$(this.getDOMNode()).datepicker()
.on('changeDate', this.onChangeDate)
.on('clearDate', this.onChangeDate);
this.setDate(this.props.date);
},
componentWillUnmount: function(){
return $(this.getDOMNode()).datepicker('remove');
},
getDatepickerDate: function(){
return $(this.getDOMNode()).datepicker('getDate');
},
setDate: function(date){
if (!this.isMounted()) {
return;
}
if (moment(date).isSame(this.getDatepickerDate, 'day')) {
// If there is no change between the date that
// is about to be set then just ignore and
// keep the old one.
return;
}
date = date ? moment(date).toDate() : void 8;
$(this.getDOMNode()).datepicker('setDate', date);
},
onChangeDate: function(event){
if (this.props.onChange) {
this.props.onChange(event.date);
}
},
render: function(){
return this.transferPropsTo(input({
type: 'text',
className: 'form-control'
}));
}
});
If in case here is the store:
ReservationFormStore = fluxxor.createStore({
actions: {
SET_RESERVATION_TYPE: 'setReservationType',
SET_RESERVATION_PICKUP_TIME: 'setPickupTime'
},
initialize: function(){
this.reservationType = void 8;
this.pickupTime = moment().add('minutes',30);
},
setReservationType: function(reservationType){
this.reservationType = reservationType;
this.reservationTypeValidate = true;
this.emit('change-reservation-type', this.reservationType);
this.emit('change');
}
setPickupTime: function(pickupTime){
this.pickupTime = pickupTime;
this.pickupTimeValidate = true;
this.emit('change-pickup-time', this.pickupTime);
this.emit('change');
}
});

Update react component using postal.js

I am trying to use postal.js subscribe/publish data in my reactJs site, I am currently doing this. Can anyone tell me how to push the selected id, I think the loadContacts method is resetting the value to false:
This is my top level page:
// load initial contacts into page
loadContacts: function() {
var page = this;
ContactDirectoryService.getContacts(this.state.pageNumber, function(response) {
var contacts = response.contacts.map(function(contact){
contact.isSelected = false;
return contact;
});
page.setState({ contacts: contacts });
});
},
// postal subscribe to receive publish
componentDidMount: function() {
this.loadContacts();
var page = this;
contactChannel.subscribe("selectedContact", function(data, envelope) {
page.handleSelectedContact(data.id, page);
});
},
handleSelectedContact: function(id, page) {
var page = this;
// service to add contact using api call
BasketService.addPerson(id, function () {
console.log(id);
var arrayPush = [];
var arrayPush = page.state.selectedContacts.slice();
// push selected id to selectedContacts array
arrayPush.push(id);
page.setState({selectedContacts: arrayPush})
//add is selected to contacts
page.setState({ contacts: contacts });
// push selected id which isn't working
for(var i=0;i<page.state.contacts.length;i++)
{
var idAsNumber = parseInt(id);
if (page.state.contacts[i].id === idAsNumber) {
page.state.contacts[i].isSelected = true;
break;
}
}
basketChannel.publish({
channel: "basket",
topic: "addContactToBasket",
data: {
id: id,
newTotal: arrayPush.length
}
});
});
},
addContactToBasket: function(selectedId) {
console.log('Add ID');
console.log('Add ID');
BasketService.addPerson(selectedId, function () {
var arrayPush = [];
var arrayPush = this.state.selectedContacts.slice();
arrayPush.push(selectedId);
this.setState({selectedContacts: arrayPush})
person.isSelected = true;
basketChannel.publish({
channel: "basket",
topic: "addContactToBasket",
data: {
id: selectedId,
newTotal: arrayPush.length
}
});
});
},
Checkbox component page, to select id and publish to selectedContact channel
handler: function(e) {
e.target.value;
e.preventDefault();
channel.publish({
channel: "contact",
topic: "selectedContact",
data: {
id: e.target.attributes['data-ref'].value
}
});
},
render: function() {
return (
<div className="contact-selector">
<input type="checkbox"
checked={this.props.data.isSelected}
onChange={this.handler} />
</div>
);
},
You're passing a cached context as page, however in the first line of handleSelectedContact() you're also reinitialising the argument page into a fresh local copy of this.

I am having trouble passing in my backbone collection in to a react component

my backbone collection collection doesn't populate when i just pass it in as props to a react component. I have tried first fetching the collection using componentDidmount and componentWillMount, but that still didn't populate the collection. If I test the code by setting a window variable pointing to DecksIndex and in the console tools call getInstance() and then fetch
,the data loads fine. my code is as follows:
//router.js
var DeckComponent = require("./views/deck.jsx")
var DecksIndex = React.createFactory(require("./views/decks.jsx"))
var decksCollection = require("./component/collections/decks.js");
module.exports = Backbone.Router.extend({
initialize: function(){
this.rootEl = document.getElementById('container');
},
routes: {
"":"index",
"decks/:id":"deckShow"
},
index: function(){
var decks = new DecksIndex({decks: decksCollection.getInstance()});
this._swapView(decks)
console.log("hooray!")
},
deckShow: function(id){
//var deck = Flashcards.Collections.decks.getOrFetch(id);
var showDeck = new DeckComponent();
this._swapView(showDeck);
},
_swapView: function(view){
if (this.currentView) {
React.unmountComponentAtNode(this.rootEl);
}
this.currentView = view
React.render(view, document.getElementById('container'));
}
});
//decks.js
var deck = require('../models/deck.js')
var decks = Backbone.Collection.extend({
url: "/api/decks",
model: deck,
getOrFetch: function(id){
var model = this.get(id);
var that = this;
if (model) {
model.fetch();
}else{
model = new deck({id: id})
model.fetch({
success: function(){
that.add(model)
}
})
}
return model;
},
parse: function (data) {
debugger;
return data.objects
},
});
decks.getInstance = _.memoize(function () {
return new decks();
});
module.exports = decks;
//decks.jsx
var DecksList = React.createClass({
render: function() {
return (
<div className="deck-list">
{
this.props.decks.map(function (deck) {
var title = deck.name
debugger;
return (
<div key={deck.id} className="note-summary">
{title}
</div>
);
})
}
</div>
);
}
});
module.exports = DecksList;
this is an example of a situation where a container component that manages state makes sense. If DecksList had a container that retrieved the collection when it mounted and only rendered DecksList once the data was available it would probably solve the problem. Here's a good article on the pattern: https://medium.com/#dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0

Reflux event not triggering

I'm working on a React app with Reflux and I'm having a problem connecting a store to a component.
Here's the code
// src/stores/post.js
var Reflux = require('reflux');
var $ = require('jquery');
var PostActions = require('../actions/post');
module.exports = Reflux.createStore({
init: function() {
this.listenTo(PostActions.vote, this.onVote);
},
getInitialData: function() {
return {
title: "Post 1",
content: "This is a post!",
voteCount: 6
}
},
onVote: function(postId, studentId, voteValue) {
this.trigger();
console.log("VOTE ACTION TRIGGERED");
}
});
// src/actions/post.js
var Reflux = require('reflux');
module.exports = Reflux.createActions([
"vote"
]);
// src/components/posts/upvote.js
var React = require('react');
var Reflux = require('reflux');
var PostStore = require('../../stores/post');
var PostActions = require('../../actions/post');
module.exports = React.createClass({
mixins: [Reflux.ListenerMixin],
getInitialState: function() {
return {
voteCount: this.props.votes
}
},
componentDidMount: function() {
this.listenTo(PostStore, this.onVoteCountChange);
},
componentWillUnmount: function() {
this.unsubscribe();
},
onVoteCountChange: function(newVoteCount) {
this.setState({
voteCount: newVoteCount
});
},
handleClick: function() {
console.log(PostActions);
PostActions.vote(
null, null, null
);
},
render: function() {
return (
<div className="votes">
<p>{this.state.voteCount}</p>
<span className="glyphicon glyphicon-chevron-up"
onClick={this.handleClick}></span>
</div>
)
}
});
The problem is, the code works when I run it in the Node console:
> var PostStore = require('./src/stores/post');
undefined
> var PostActions = require('./src/actions/post');
undefined
> PostActions.vote(null, null, null);
undefined
> VOTE ACTION TRIGGERED
But when I run the tests, the event doesn't get logged. However, I know the click is happening because handleClick() is being called and the PostActions object is being printed to the console.
The PostStore is also being initialized (I had a console.log() in there to verify it). This leads me to believe that somehow the problem is in the React component, but as far as I can tell my code looks exactly like what's in the Reflux documentation.
Also, as an aside, is there a better way for me to debug my code during the Jest tests than with a bunch of console.log() calls thrown everywhere? Something like binding.pry in ruby?
EDIT: I'm including the tests:
jest.dontMock('../../../src/components/posts/upvote');
jest.dontMock('../../../src/actions/post.js');
jest.dontMock('../../../src/stores/post.js');
describe('Upvote', function() {
var React = require('react/addons');
var Upvote = require('../../../src/components/posts/upvote');
var TestUtils = React.addons.TestUtils;
var upvote;
beforeEach(function() {
upvote = TestUtils.renderIntoDocument(
<Upvote postId="1" votes="6"/>
);
});
it('should display the correct upvote count', function() {
var votes = TestUtils.findRenderedDOMComponentWithTag(
upvote, "p"
).getDOMNode().textContent;
expect(votes).toEqual("6");
});
it('should handle upvote clicks', function() {
var upArrow = TestUtils.findRenderedDOMComponentWithTag(
upvote, "span"
).getDOMNode();
TestUtils.Simulate.click(upArrow);
// var votes = TestUtils.findRenderedDOMComponentWithTag(
// upvote, "p"
// ).getDOMNode().textContent;
// expect(votes).toEqual("7");
});
});
So as it turns out, I had two problems. The first one was that reflux was being mocked automatically. The second had to do with actions and timers, and I found the solution here.
I'm gonna post my code anyways:
// gulpfile.js
// the config is used for gulp-jest
var jestConfig = {
"scriptPreprocessor": "./helpers/jsx-preprocessor.js", // relative to gulp.src
"unmockedModulePathPatterns": [
"../node_modules/react",
"../node_modules/reflux" // this is where reflux gets unmocked
]
}
// __tests__/upvote.js
it('should handle upvote clicks', function() {
var upArrow = TestUtils.findRenderedDOMComponentWithTag(
upvote, "span"
).getDOMNode();
TestUtils.Simulate.click(upArrow);
jest.runAllTimers(); // this is where the magic happens
var votes = TestUtils.findRenderedDOMComponentWithTag(
upvote, "p"
).getDOMNode().textContent;
expect(votes).toEqual("7");
});

Categories