Hello,
See #315 for the start of this conversation.
I'm running into an error when I try to pass an array of objects to a tested, mounted component as a prop.
Here is the component:
import * as React from 'react';
import { ReferenceWindowEvents as LocalEvents, FieldMappings } from '../../utils/Constants';
interface PeopleProps {
people: CSL.TypedPerson[]
eventHandler: Function
citationType: CSL.CitationType
}
export class People extends React.Component<PeopleProps,{}> {
public fieldMaps: ABT.FieldMappings = FieldMappings;
constructor(props) {
super(props);
}
addPerson() {
this.props.eventHandler(
new CustomEvent(LocalEvents.ADD_PERSON)
);
}
removePerson(e: InputEvent) {
this.props.eventHandler(
new CustomEvent(LocalEvents.REMOVE_PERSON, {
detail: parseInt(e.target.dataset['num']),
})
);
}
onChange(e: InputEvent) {
this.props.eventHandler(
new CustomEvent(LocalEvents.PERSON_CHANGE, {
detail: {
index: parseInt(e.target.dataset['num']),
field: e.target.dataset['namefield'],
value: e.target.value,
}
})
);
}
render() {
return (
<div>
<div className='row' style={{ display: 'flex', alignItems: 'center', }}>
<strong style={{ paddingRight: 5, }} children='Contributors'/>
<input
type='button'
id='add-person'
className='btn'
value='Add Another'
onClick={this.addPerson.bind(this)}/>
</div>
{this.props.people.map((person: CSL.TypedPerson, i: number) =>
<div key={`person-list-${i}`} style={{ display: 'flex', alignItems: 'center', }}>
<div>
<select
value={person.type}
data-num={i}
data-namefield='type'
onChange={this.onChange.bind(this)}>
{ this.fieldMaps[this.props.citationType].people.map((p, j: number) =>
<option key={`peopleSelect-${j}`} value={p.type} children={p.label} />
)}
</select>
</div>
<div style={{ flex: 1, padding: '0 5px', }} >
<input
type='text'
data-namefield='family'
data-num={i}
style={{ width: '100%', }}
placeholder='Lastname'
aria-label='Last Name'
id={`person-family-${i}`}
value={person.family}
onChange={this.onChange.bind(this)}
required={true} />
</div>
,
<div style={{ flex: 1, padding: '0 5px', }} >
<input
type='text'
data-namefield='given'
style={{width: '100%'}}
placeholder='Firstname, Middleinitial'
aria-label='First Name, Middle Initial'
data-num={i}
id={`person-given-${i}`}
value={person.given}
onChange={this.onChange.bind(this)}
required={true} />
</div>
<div style={{ padding: '0 5px', }}>
<input
type='button'
className='btn'
data-num={i}
value='✖'
onClick={this.removePerson.bind(this)} />
</div>
</div>
)}
</div>
)
}
}
Here is the test file
jest.unmock('../People');
import * as React from 'react';
import { shallow, mount } from 'enzyme';
import * as sinon from 'sinon';
import { People } from '../People';
function setup(citationType: CSL.CitationType = 'article') {
const eventHandler = sinon.spy();
const component = shallow(
<People citationType={citationType} eventHandler={eventHandler} people={[{type: 'author', family: 'adsf', given: 'asdfad'}]} />
);
return {
eventHandler,
component,
addButton: component.find('#add-person')
}
}
describe('<People />', () => {
it('should dispatch ADD_PERSON event when add button is clicked', () => {
// this is where the error is thrown
const { addButton, eventHandler } = setup();
// rest of file truncated, since it's not relevant
Here is the error I receive:
<People /> › it should dispatch ADD_PERSON event when add button is clicked
- TypeError: Cannot read property 'people' of undefined
at eval (lib/js/components/ReferenceWindow/People.tsx:9:3188)
at Array.map (native)
at People.render (lib/js/components/ReferenceWindow/People.tsx:9:2795)
at ReactCompositeComponentMixin._renderValidatedComponentWithoutOwnerOrContext (node_modules/enzyme/node_modules/react/lib/ReactCompositeComponent.js:587:34)
at ReactCompositeComponentMixin.mountComponent (node_modules/enzyme/node_modules/react/lib/ReactCompositeComponent.js:220:30)
at wrapper [as mountComponent] (node_modules/enzyme/node_modules/react/lib/ReactPerf.js:66:21)
at ReactShallowRenderer._render (node_modules/enzyme/node_modules/react/lib/ReactTestUtils.js:366:14)
at _batchedRender (node_modules/enzyme/node_modules/react/lib/ReactTestUtils.js:348:12)
at ReactDefaultBatchingStrategyTransaction.Mixin.perform (node_modules/enzyme/node_modules/react/lib/Transaction.js:136:20)
at Object.ReactDefaultBatchingStrategy.batchedUpdates (node_modules/enzyme/node_modules/react/lib/ReactDefaultBatchingStrategy.js:62:19)
at Object.batchedUpdates (node_modules/enzyme/node_modules/react/lib/ReactUpdates.js:94:20)
at ReactShallowRenderer.render (node_modules/enzyme/node_modules/react/lib/ReactTestUtils.js:343:16)
at render (node_modules/enzyme/build/react-compat.js:146:39)
at new ShallowWrapper (node_modules/enzyme/build/ShallowWrapper.js:81:21)
at Object.shallow (node_modules/enzyme/build/shallow.js:21:10)
at setup (lib/js/components/ReferenceWindow/__tests__/People-test.tsx:10:30)
at Object.eval (lib/js/components/ReferenceWindow/__tests__/People-test.tsx:19:18)
at attemptSync (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1791:24)
at QueueRunner.run (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1779:9)
at QueueRunner.execute (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1764:10)
at Spec.Env.queueRunnerFactory (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:629:35)
at Spec.execute (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:355:10)
at Object.fn (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:2362:37)
at attemptAsync (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1821:24)
at QueueRunner.run (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1776:9)
at QueueRunner.execute (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1764:10)
at Env.queueRunnerFactory (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:629:35)
at Object.fn (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:2347:13)
at attemptAsync (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1821:24)
at QueueRunner.run (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1776:9)
at QueueRunner.execute (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:1764:10)
at Env.queueRunnerFactory (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:629:35)
at TreeProcessor.execute (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:2211:7)
at Env.execute (node_modules/jest-jasmine2/vendor/jasmine-2.3.4.js:680:17)
at jasmine2 (node_modules/jest-jasmine2/src/index.js:194:7)
at handle (node_modules/worker-farm/lib/child/index.js:41:8)
at process.<anonymous> (node_modules/worker-farm/lib/child/index.js:47:3)
at emitTwo (events.js:100:13)
at process.emit (events.js:185:7)
at handleMessage (internal/child_process.js:718:10)
at Pipe.channel.onread (internal/child_process.js:444:11)
Here is my package.json
Note: I added everything below sinon in the unmockedModulePathPatterns in accordance to the recommendations in the enzyme docs.
{
"name": "academic-bloggers-toolkit",
"version": "3.0.0",
"description": "A plugin extending the functionality of WordPress for Academic Blogging.",
"main": "index.js",
"scripts": {
"test": "jest",
"wp": "wpcmd() { docker exec $(docker ps -lq) /bin/bash -c \"sudo -u www-data wp $(echo $@)\"; };wpcmd "
},
"repository": {
"type": "git",
"url": "git+https://github.com/dsifford/academic-bloggers-toolkit.git"
},
"author": "Derek P Sifford",
"license": "GPL-3.0",
"bugs": {
"url": "https://github.com/dsifford/academic-bloggers-toolkit/issues"
},
"homepage": "https://github.com/dsifford/academic-bloggers-toolkit#readme",
"devDependencies": {
"babel-core": "^6.7.6",
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"browser-sync": "^2.12.5",
"coveralls": "^2.11.9",
"del": "^2.2.0",
"enzyme": "^2.2.0",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.0",
"gulp-clean-css": "^2.0.6",
"gulp-sass": "^2.3.1",
"gulp-uglify": "^1.5.3",
"jest-cli": "^11.0.2",
"ntypescript": "^1.201604262206.1",
"react": "^15.0.1",
"react-addons-test-utils": "^15.0.1",
"react-dom": "^15.0.1",
"sinon": "^1.17.3",
"ts-loader": "^0.8.2",
"typescript": "^1.9.0-dev.20160423",
"webpack": "^1.13.0",
"webpack-stream": "^3.2.0"
},
"jest": {
"scriptPreprocessor": "jestPreprocessor.js",
"testFileExtensions": [
"ts",
"tsx",
"js"
],
"moduleFileExtensions": [
"js",
"ts",
"tsx",
"json"
],
"unmockedModulePathPatterns": [
"react",
"react-dom",
"react-addons-test-utils",
"fbjs",
"enzyme",
"sinon",
"cheerio",
"htmlparser2",
"underscore",
"lodash",
"domhandler",
"object.assign",
"define-properties",
"function-bind",
"object-keys",
"es-abstract",
"object.values"
],
"verbose": true,
"collectCoverage": true
},
"dependencies": {}
}
If you can offer any help/tips, they would be much appreciated. Thank you very much in advance 😄
Final note: I suppose I should mention that everything works fine and as expected pass an empty array to <People /> as the people prop.
Is it possibly this.fieldMaps[this.props.citationType].people.map? Is this.fieldMaps being loaded correctly in your testing environment?
What do you get if you log out this.props at the top of your render method?
Standby, I'll give that a look now.
Of note, I tried jest.unmocking the fieldmapping object as well yesterday and that didn't seem to have any effect.
@Aweary Good observation...
The props are received just fine, however this.fieldMappings seems to only go one level deep...
Here is a look at what I get:
{ bill: { title: 'Bill', fields: [], people: [] },
book: { title: 'Book', fields: [], people: [] },
chapter: { title: 'Book Section', fields: [], people: [] },
broadcast: { title: 'Broadcast', fields: [], people: [] },
legal_case: { title: 'Case', fields: [], people: [] },
'paper-conference': { title: 'Conference Proceeding', fields: [], people: [] },
'entry-encyclopedia': { title: 'Encyclopedia Entry', fields: [], people: [] },
motion_picture: { title: 'Film', fields: [], people: [] },
speech: { title: 'Presentation', fields: [], people: [] },
'article-journal': { title: 'Journal Article', fields: [], people: [] },
'article-magazine': { title: 'Magazine Article', fields: [], people: [] },
'article-newspaper': { title: 'Newspaper Article', fields: [], people: [] },
patent: { title: 'Patent', fields: [], people: [] },
report: { title: 'Report', fields: [], people: [] },
legislation: { title: 'Statute', fields: [], people: [] },
thesis: { title: 'Thesis', fields: [], people: [] },
webpage: { title: 'Web Page', fields: [], people: [] } }
and here's a small snippet of how the first two fieldmaps should look:
export const FieldMappings: ABT.FieldMappings = {
bill: {
title: 'Bill',
fields: [
{ value: 'title', label: 'Title', required: true, pattern: '.*', placeholder: '', },
{ value: 'number', label: 'Bill Number', required: false, pattern: '[0-9]+', placeholder: '', },
{ value: 'page', label: 'Code Pages', required: false, pattern: '^[0-9]+-?[0-9]*$', placeholder: 'Number or Range of Numbers (100-200)', },
{ value: 'volume', label: 'Code Volume', required: false, pattern: '[0-9]+', placeholder: '', },
{ value: 'section', label: 'Section', required: false, pattern: '.*', placeholder: '', },
{ value: 'publisher', label: 'Legislative Body', required: false, pattern: '.*', placeholder: '', },
{ value: 'issued', label: 'Date', required: true, pattern: '[0-9]{4}(\/[0-9]{2})?(\/[0-9]{2})?(?!\/)$', placeholder: 'YYYY/MM/DD or YYYY/MM or YYYY', },
{ value: 'accessed', label: 'Date Accessed', required: false, pattern: '[0-9]{4}(\/[0-9]{2})?(\/[0-9]{2})?(?!\/)$', placeholder: 'YYYY/MM/DD or YYYY/MM or YYYY', },
],
people: [
{ type: 'author', label: 'Sponsor', },
],
},
book: {
title: 'Book',
fields: [
{ value: 'title', label: 'Title', required: true, pattern: '.*', placeholder: '', },
{ value: 'collection-title', label: 'Series Title', required: false, pattern: '.*', placeholder: '', },
{ value: 'collection-number', label: 'Series Number', required: false, pattern: '[0-9]+', placeholder: '', },
{ value: 'number-of-pages', label: '# of Pages', required: false, pattern: '[0-9]+', placeholder: '', },
{ value: 'volume', label: 'Volume', required: false, pattern: '[0-9]+', placeholder: '', },
{ value: 'edition', label: 'Edition', required: false, pattern: '[0-9]+', placeholder: '', },
{ value: 'publisher', label: 'Publisher', required: true, pattern: '.*', placeholder: '', },
{ value: 'publisher-place', label: 'Publisher Location', required: false, pattern: '.*', placeholder: '', },
{ value: 'issued', label: 'Date', required: true, pattern: '[0-9]{4}(\/[0-9]{2})?(\/[0-9]{2})?(?!\/)$', placeholder: 'YYYY/MM/DD or YYYY/MM or YYYY', },
{ value: 'accessed', label: 'Date Accessed', required: false, pattern: '[0-9]{4}(\/[0-9]{2})?(\/[0-9]{2})?(?!\/)$', placeholder: 'YYYY/MM/DD or YYYY/MM or YYYY', },
],
people: [
{ type: 'author', label: 'Author', },
{ type: 'editor', label: 'Editor', },
{ type: 'collection-editor', label: 'Series Editor', },
{ type: 'translator', label: 'Translator', },
],
},
Thoughts?
Update: I unmocked the file that contained the fieldmaps and they're now being sent fully. However, the error still persists.
Update again: I'm embarrassed. Found a typo. Unmocking solved part 1 of the issue. Fixing the typo solved the other.
Closing the issue.

Most helpful comment
Update again: I'm embarrassed. Found a typo. Unmocking solved part 1 of the issue. Fixing the typo solved the other.
Closing the issue.