Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
maiya

Configure to test in Mocha and also run on browser

Recommended Posts

Hi -

 

I am working on an app that uses GSAP to do animation inside of React class components.

 

I started the app with a boilerplate that already had webpack, babel, and mocha set up.  When I run the app on a browser, it works fine;  but when I try to run the Mocha tests, it seems that it is breaking because the webpack/babel is not set up to compile any ES6 in the node_modules folder (?).  I am beginner-level at webpack and babel.  I get the basic concepts of these two tools, but all of my attempts at fixing this problem have lead to my app breaking in one way or another.  

 

The closest I got to fixing it: the tests were running fine and it was running in the browser, but every single time I made any change whatsoever to my files, I had to run the build script (which takes a long time), in order for the change to show up in the browser.  (I'm guessing that the 'watch' option might have been affected? But don't know how to fix that).  I've been playing around with different versions and different syntax, and I'm basically lost.  When I looked at a few other modules in the node_modules folder, it seemed like they had their own internal builds or were written with standard module.export = {} syntax. 

 

Is it possible to get help with this issue?  I'm trying to not break my app, and be able to test it with Mocha and Chai.

 

Basic folder structure:

 

- /client

- /node_modules

- /server

- package.json

- webpack.config.js

- .babelrc

 

gsap version:

"gsap": "^2.1.3",

 

start and test and build scripts in package.json

"build-client": "webpack",
"start": "node server",
"start-dev": "NODE_ENV='development' npm run build-client-watch & NODE_ENV='development' npm run start-server",
"start-server": "nodemon server -e html,js,scss --ignore public --ignore client",
"test": "NODE_ENV='test' mocha \"./server/**/*.spec.js\" \"./client/**/*.spec.js\" \"./script/**/*.spec.js\" --require @babel/polyfill --require @babel/register"

 

import statements in React code / animation.js

import React, { Component } from 'react'
import { connect } from 'react-redux'
 
import { TimelineMax, CSSPlugin, AttrPlugin, Power0, Draggable } from 'gsap'
// without this line, CSSPlugin and AttrPlugin may get dropped by your bundler, because of 'tree shaking'
const plugins = [CSSPlugin, AttrPlugin]

 

animation.spec.js

import { expect } from 'chai'
import React from 'react'
import enzyme, { shallow } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import { Animation } from './animation'
 
const adapter = new Adapter()
enzyme.configure({ adapter })
 
describe('animation', () => {
let timeline
 
beforeEach(() => {
animation = shallow(<Animation />)
})
 
it('renders the correct text in an h3', () => {
expect(timeline.find('h3').text()).to.be.equal('this is an h3 tag')
})
})

 

error message:

Quote

/Volumes/Seagate/app_title/node_modules/gsap/all.js:12
import TweenLite, {Ease, Power0, Power1, Power2, Power3, Power4, Linear, _gsScope} from "./TweenLite.js";
       ^^^^^^^^^
SyntaxError: Unexpected identifier
    at new Script (vm.js:79:7)

 

 

 

Is there any straight forward way to fix this?  I get that the error has something to do with not being able to read the ES6 code, but I don't get how to actually fix it in the settings. 

 

Thanks for your time.

 

 

Share this post


Link to post
Share on other sites

That error looks like it is expecting umd/CommonJS modules instead of es modules. I would try loading the umd versions of GSAP.

 

import { TimelineMax, Power0 } from "gsap/umd/TweenMax"; 
import Draggable from "gsap/umd/Draggable";

 

  • Like 2

Share this post


Link to post
Share on other sites

Hi - I just saw this reply! Thanks.  I will give it a try in the a.m.

Share this post


Link to post
Share on other sites

Hi Blake, I tried to change it to the umd version, and it's still not working.  Now I'm getting this error below in the console when I run it in the browser.  I tried importing the CSS and AttrPlugin, because that fixed this error for another user, but the error is still throwing.

 

animation.js:184 Uncaught TypeError: gsap_umd_TweenMax__WEBPACK_IMPORTED_MODULE_1__.TimelineMax is not a constructor

 

It's also not working in the test file.  It throws this error:

TypeError: Cannot add property _gsTweenID, object is not extensible

 

Share this post


Link to post
Share on other sites
1 hour ago, maiya said:

Hi Blake, I tried to change it to the umd version, and it's still not working.  Now I'm getting this error below in the console when I run it in the browser.  I tried importing the CSS and AttrPlugin, because that fixed this error for another user, but the error is still throwing.

 

What version of webpack are you using? v4 has problems mixing es modules with umd. v3 doesn't. Also, have you tried using require() instead of import for the umd? 

 

If you go back to using es modules, try letting babel transpile gsap in your node_modules folder. See if this post is any help.

 

 

 

1 hour ago, maiya said:

It's also not working in the test file.  It throws this error:


TypeError: Cannot add property _gsTweenID, object is not extensible

 

 

_gsTweenID is added to every object/element it animates. The only way I can see that error happening is if what you're trying to animate isn't an object, but a primitive like a string or number.

 

Setting up build environments can be a real PITA sometimes. Have you considered using something pre-made, like Create React App? 

https://github.com/facebook/create-react-app

 

  • Like 1

Share this post


Link to post
Share on other sites

Yes, the boilerplate is using webpack 4.   I just changed it to 3.12.0 to see if it would be a quick-fix, but this broke the webpack configuration (which is set up for v4). Got this error.  I'm not comfortable with webpack and babel to try to reconfigure everything.  I don't know what other things it might break, etc.  

 

Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration has an unknown property 'mode'. These properties are valid:
   object { amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry, externals?, loader?, module?, name?, node?, output?, parallelism?, pe
rformance?, plugins?, profile?, recordsInputPath?, recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, stats?, target?, watch?, watchOptions? }
   For typos: please correct them.
   For loader options: webpack 2 no longer allows custom properties in configuration.
     Loaders should be updated to allow passing options via loader options in module.rules.
     Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:
     plugins: [
       new webpack.LoaderOptionsPlugin({
         // test: /\.xxx$/, // may apply this only for some modules
         options: {
           mode: ...
         }
       })
     ]

 

Thank you for the links.  I will check them out this eve & try the require statement too.  Hopefully just loading it as a CDN (mentioned in post you linked) will solve the issue.  

 

Share this post


Link to post
Share on other sites
34 minutes ago, OSUblake said:
34 minutes ago, OSUblake said:

Setting up build environments can be a real PITA sometimes. Have you considered using something pre-made, like Create React App? 

https://github.com/facebook/create-react-app

 

 

I've played w/ create-react-app, but I sometimes got CORS errors when making requests to the server, even when I set up a proxy property in the package.json as they say to do.  So I'm using this boilerplate, since puts all of my code on one port (and it's got the passport/login code and sequelize/database boilerplate ready to go).  I wish there was something that would generate both front-end and back-end boilerplate, like they have in rails!  

Share this post


Link to post
Share on other sites

The CDN is working well for the client-side code, but the tests do not have access to the index.html, where the cdn linked, so it just says "TimelineMax is undefined."  I tried changing my webpack to include the node modules, and add the require/import statements when running tests;  and comment out the require statements/rely on the CDN when running in the browser, but that didn't work either.  I think I just don't have enough understanding of webpack and babel at this point.  (I'm going down rabbit holes).  Would it be useful for me to link to the repo of the boilerplate I'm using?

Share this post


Link to post
Share on other sites

You might be able to import from a CDN in your test like this:

 

import { TimelineMax } from "https://unpkg.com/gsap@2.1.3/all.js?module";

 

Or maybe, like this:

 

import { TimelineMax } from "https://unpkg.com/gsap@2.1.3/all.js";

 

Really not sure how webpack will treat those. You may need to link to the umd versions.

 

Those are coming from the unpkg cdn. 

https://unpkg.com/

https://unpkg.com/browse/gsap@2.1.3/

 

  • Like 1

Share this post


Link to post
Share on other sites

Hi -  That's not working either.  For the tests, its saying that it can not locate module:  "https://unpkg.com/gsap@2.1.3/all.js"  ( tried both).  Tried adding an "external" property to the webpack.config  but still throwing errors.  I think there is just something that I don't understand at this point. 

Share this post


Link to post
Share on other sites
8 hours ago, maiya said:

Would it be useful for me to link to the repo of the boilerplate I'm using?

 

Yes.

  • Like 1

Share this post


Link to post
Share on other sites

Thanks. This is the repo.  https://github.com/FullstackAcademy/boilermaker 

 

Here's some quick starter-code, to add an animation 

 

gsap in a class component:

/client/components/my-animation.js

 

import React, {Component} from 'react'
import {connect} from 'react-redux'

 

import {CSSPlugin, AttrPlugin, TimelineMax} from 'gsap/all'
const plugins = [CSSPlugin, AttrPlugin]

 

export class MyAnimation extends Component {
          constructor(props) {
                    super(props)
                   this.state = {
                            animationTimeline: null
                    }
                   // sets things up, so you can store an actual dom element to this variable
                   this.element = React.createRef()        
          }
 
componentDidMount ()  {
          this.setState({animationTimeline: this.getAnimationTimeline()})
}
 
getAnimationTimeline = () => {
         const tl = new TimelineMax({repeat: -1})
          tl.to(this.element, 1, {rotation: '+=360'}, 0)
         return tl
}

 

render() {
          return (
                  <React.Fragment>
                   <h3>animation header</h3>
                    <div style={{width: '100%', border: 'solid black 10px'}}>
                           <button
                                    type="button"
                                     onClick={() => { 
                                         let timeline = this.state.animationTimeline
                                         timeline.play()}
                                       }>
                       run animation
                  </button>
                   <div className="animate-this-div"
                           //this ref property works in conjunction with the React.createRef() code in the constructor:
                            ref={div => this.element = div}}
                            style={{width: '5rem', height: '5rem', border: 'solid magenta 3px'}}
                    />
                  </div>
                <React.Fragment>
                )
      }
}

 

const mapStateToProps = state => {
                  return {}
}

 

export default connect(mapStateToProps)(MyAnimation)

 

Then, in client/components/index.js, add this:

export {default as MyAnimation} from './my-animation.js'

 

Then, import the animation component into the routes file:

Then, in client/components/routes.js, 

import { Login, Signup, UserHome,  MyAnimation} from './components'
 
....
 
<Route path="/my-animation" component={MyAnimation} />

 

Now, the animation should show up at http://localhost:8080/my-animation

 

(All of the imports to easily add links are already in the client/components/navbar.js

 

Thanks again for looking at all this!

 

 

Share this post


Link to post
Share on other sites

Ok. I'll try to test that out later today.

 

Do I need to setup databases and OAuth for it to run? 

Share this post


Link to post
Share on other sites

Thanks! 

 

It does need a postgres database to start.  The database name is whatever you have in the package.json under the "name" field.  So, you need to add a "boilerplate" and a "boilerplate-test" database.   If you want to change the db name, you can just change the "name" field in package.json.

 

I think you can do everything without interacting with any O-Auth by just putting the route to the MyAnimation component in the part of the switch statement that does not require login (see code below).  If you want to do the log in, though, it's easy.  You just run the "seed"  script from the package.json, and it will give you some default users.

 

Steps to start up: 1) npm install 2) create postgres databases  2) npm run build-client  3) npm run start-dev

 

client/components/routes.js

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { withRouter, Route, Switch } from 'react-router-dom'
import PropTypes from 'prop-types'
import { Login, Signup, UserHome, MyAnimation } from './components'
import { me } from './store'
 
 
class Routes extends Component {
          componentDidMount() {
          this.props.loadInitialData()
}
 
          render() {
                    const { isLoggedIn } = this.props
                    return (
                            <Switch>
                                    {/* Routes placed here are available to all visitors */}
                                  <Route path="/login" component={Login} />
                                  <Route path="/signup" component={Signup} />
                                  {/* Putting animated component here will allow you to not have to log in to see it: */}
                                  <Route path="/my-animation" component={MyAnimation} />
                                  {isLoggedIn && (
                                         <Switch>
                                                {/* Routes placed here are only available after logging in */}
                                               <Route path="/home" component={UserHome} />
                                              </Switch>
                                           )}
                                 <Route component={Login} />
                          </Switch>
                          )
                        }
}

 

oh, and for anyone who needs to know about the tests..

add  /client/components/my-animation.spec.js  (in the same directory as the my-animation.js)

and run "npm test" script to run all of the .spec files w/mocha

 

 

 
import {expect} from 'chai'
import React from 'react'
import enzyme, {shallow} from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import {MyAnimation} from './my-animation'
 
const adapter = new Adapter()
enzyme.configure({adapter})
 
describe('my-animation', () => {
     let myAnimation
 
     beforeEach(() => {
          myAnimation = shallow(<MyAnimation />)
     })
 
/* just any generic test to make sure everything is compiling */
     it('renders the correct text in an h3', () => {
          expect(myAnimation.find('h3').text()).to.be.equal('animation header')
     })
})

 

 

Share this post


Link to post
Share on other sites

I just realized I should probably post a repo with greensock/ animations already set up.  I'll do that by tomorrow a.m.

Share this post


Link to post
Share on other sites

Yes, that would be helpful. I was having trouble installing boilermaker last night. It doesn't seem to like windows environments. 

 

And include a spec too.

 

Share this post


Link to post
Share on other sites

Ok.  I didn't have time last night.  Will post it today. Thanks again!

Share this post


Link to post
Share on other sites

Hi Blake,

Here is a repo with gsap dependency added,  an animating class component, and a generic mocha test set up.  I have node v10.15.1 and npm v6.4.1 running on mac.  (I also heard it's hard to make it work on windows/ fingers crossed 🤞)

 

I put the instructions on how to start it up at the top of the readme.md.   It's just set up the original way I tried/ before any experiments or trouble-shooting.  

https://github.com/maiya-22/gsap-test-repo

 

the AnimatedComponent declared (and TimelineMax, etc imported)

- client/components/animated-component.js

 

the AnimatedComponent (and all other components) exported from this file, so all imports of the component come from the same place.

- client/components/index.js

 

the route to the AnimatedComponent declared:

- client/routes.js

 

the link to the AnimatedComponent declared:

- client/components/navbar.js

 

the test spec:

- client/components/animated-component.spec.js

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×