React v16、Enzyme v3

昨日から日本に来てます。

facebook.github.io

さて、炎上の末にライセンスがMITになったReactのv16がリリースされたと同時期に、Enzymeもv3がリリース。v3からsetupに変化がありました。

Working with React 16.x · Enzyme

説明とおりにしたけど動かねー。CSSセレクターで引っ掛けてたパターンが全滅。@types/enzymeはエラー、さらにはReact本体も警告だしている。

Warning: React depends on requestAnimationFrame. Make sure that you load a polyfill in older browsers. http://fb.me/react-polyfills

React 16 JavaScript Environment.md · GitHub

いろいろ読んで解決しました。

requestAnimationFrameの対処

// package.json部分
    "jest": {
        "moduleFileExtensions": [
            "ts",
            "tsx",
            "js"
        ],
        "setupFiles": [
            "raf/polyfill",
            "./tools/enzyme.setup.js"
        ],
        "transform": {
            "^.+\\.(ts|tsx)$": "./node_modules/ts-jest/preprocessor.js"
        },
        "testMatch": [
            "**/__tests__/*((Test|Spec)\\.(ts|tsx))"
        ]
    },

yarn add raf -D をしたあとで、package.jsonのjest.setupFilesに、raf/polyfillを追加すると警告は消える。rafはレガシーブラウザでrequestAnimationFrameを埋めるライブラリ。JsDOMがEnzymeの依存で入ってくる古いものだったので、試みに最新に上げてもみましたが、このrequestAnimationFrameは手当しないとダメだった。

@types/enzymeの対処

node_modulesの中の@types/enzymeを見ると、自身の腹にnode_modules/@types/reactを抱え込んでいた。これが悪影響するので抱え込んでるnode_modulesを削除で、問題解決。デプロイのミスかな?

Enzyme v3へのマイグレーション

Migration from 2.x to 3.x · Enzyme

まず説明されてることとして、Jest起動時に走らせるsetupでEnzymeのアダプターなるものを導入する。

// ./tools/enzyme.setup.js
// Enzyme v3かつJsDOM v11
var Enzyme = require('enzyme');
var Adapter = require('enzyme-adapter-react-16');

Enzyme.configure({ adapter: new Adapter() });

const { JSDOM } = require('jsdom');

const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const { window } = jsdom;

function copyProps(src, target) {
    const props = Object.getOwnPropertyNames(src)
        .filter(prop => typeof target[prop] === 'undefined')
        .map(prop => Object.getOwnPropertyDescriptor(src, prop));
    Object.defineProperties(target, props);
}

global.window = window;
global.document = window.document;
global.navigator = {
    userAgent: 'node.js',
};
copyProps(window, global);

「the internal implementation of enzyme has been almost completely rewritten.」とのことですが説明されていたことは私の問題には関係なく、調べて見ると次と同じ現象?

Unable to parse selectors with numeric values in exponential notation · Issue #1155 · airbnb/enzyme · GitHub

テスト対象のノードを取りやすくするために、id="0"、id="1"、...、id="7"とidに数字を使ってたのですが、form.find('#0')とすると爆発。これはidをid="zero"、id="one"、...、id="seven"と文字で振り直して、form.find('#zero')とすればOK。その後にIDは数字で始まっちゃダメなルールだということをスレッドで教わりました。v2まで通ってたほうがバグでv3で修正されたとの結論。

Reactライクライブラリ

EnzymeのアダプターはReactの各バージョンの挙動の違いに対応するものを整理したと同時に、将来はReactライクライブラリの対応もしたいとのことが書いてありました。PreactやInfernoってこのマイグレーションガイドで初めてしりましたが面白そう。material-uiやreact-routerなどエコシステムのこともあるので近日の現実解ではないけど、JSX変換や基本的なAPIのところをカバーして差し替え可能にするというのはこのEnzymeだけでなく、TypescriptやBabelにもすでにあるので将来無謀なことではないように思います。

GitHub - developit/preact: ⚛️ Fast 3kb React alternative with the same ES6 API. Components & Virtual DOM.

GitHub - infernojs/inferno: An extremely fast, React-like JavaScript library for building modern user interfaces