The worry-free way to convert Coffeescript to JSX
The UI of Keboola Connection dates back to 2015 and some React components are written in Coffeescript. We decided to rewrite those components to JSX.
Since there’s no converter which is able to do this job automatically (or with a satisfying result), we agreed to do this manually, in small steps. When we need to change (or fix) something in .coffee
file, we rewrite it to .jsx
, if possible.
Let’s call a React component a blackbox. This blackbox is responsible for one job — render a React tree (basically a HTML). That so-called HTML output may differ depending on passed props, so we want to stabilize this behavior and be sure that it will not change over time (especially while we’re rewriting to JSX).
So how we do it? By writing tests.
We’re using Jest library for running tests. In this case one special feature will help us to test the blackbox behavior mentioned above — Snapshot Testing. By creating a snapshot of the component output and testing new output against snapshot we’re able to tell if component is working as before.
I decided to rewrite CurrentUser
component. It’s responsible for rendering user information (avatar, name and email) and simple “dropdown” with menu (not so important right now).
So I wrote a test for it.import React from 'react';
import CurrentUser from './CurrentUser';
import { fromJS } from 'immutable';
// few variables defined here, used later to pass as props
describe('<CurrentUser />', function() {
it('should render 40x40 icon, with email, no admin links, no maintainers, dropup false (no mode passed)', function() {
shallowSnapshot(
<CurrentUser
user={user}
maintainers={fromJS([])}
urlTemplates={urlTemplates}
canManageApps={false}
dropup={true}
/>
);
});
// more tests here, not mentioned for simplicity
});
The shallowSnapshot
function is responsible for rendering a snapshot and comparing it to the existing one. The first run of the test will create a snapshot (a .snap
file) which will be stored in the __snapshots__
directory.
Here’s preview of a snapshot:exports[`<CurrentUser /> should render 40x40 icon, with email, no admin links, no maintainers, dropup false (no mode passed) 1`] = `
<div
className="kbc-user"
onClick={[Function]}>
<img
className="kbc-user-avatar"
height={40}
src="/user.png"
width={40} />
<div>
<strong>
dev user
</strong>
<DropdownButton
...
</div>
`;
Jest has a watch mode. It’s a mode, in which tests are executed every time the change of a related files is detected.
We start this mode by running the yarn tdd
command (our alias for jest
with --watch
parameter).
And then, the boring part begins, from this point we can start rewriting to JSX.
After few iterations of seeing the tests failing, we should end up with a new JSX file and seeing tests passing again.
TL;DR
- Write tests and make sure you covered enough cases — especially cases without passing optional props
- Run Jest in watch mode (first run creates snapshots)
- Rewrite until seeing tests passing again
- Profit! :)