GitHub上でSwaggerをプレビューできるChrome拡張機能を作った 

# 概要

GitHub 上で、Swagger の yaml | yml | json を Swagger-UI に変換できる Chrome 拡張機能を作った。
その紹介。

「Swagger とは？」という話は、下記等を参照のこと。

-   [Swaggerの概要をまとめてみた。 - Qiita](https://qiita.com/gcyata/items/342073fa7607fd4082bd)

## Demo

クリックするだけ。簡単に変換できる。
![README-Demo_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170621/6a48e412-b0db-b05f-6f5d-b41e2b8f3d8b.gif)

全展開・全折り畳みができて便利。
![README-Demo_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170621/aca18218-c825-fc30-d92f-9c3b9a9fbb41.gif)

## インストール

Chrome Web Store からインストールできる。
[swagger-viewer - Chrome ウェブストア](https://chrome.google.com/webstore/detail/swagger-viewer/nfmkaonpdmaglhjjlggfhlndofdldfag)


## 特徴

### 簡単

-   1クリックで変換できる

### 依存なし

-   この拡張機能のみで動作する
-   Web の Swagger Editor を開いたり、ドキュメント生成サーバーを起動する必要なし

### セキュア（たぶん）

-   外部ネットワークへの送受信はないため、業務でも使用できる
    -   ※自己責任でお願いします
-   たぶん
    -   Chrome の Network タブを眺めたが、通信する様子はなかった
    -   Swagger の描画のために使用しているライブラリ「[swagger-ui](https://github.com/swagger-api/swagger-ui)」のコードも軽く読んだが、ネットワーク送受信に関するコードはなかった（自信なし）
        -   やべーやつだったらご連絡いただけると幸いです

# 開発裏話(?)

動けばいいんだよ2割、美しく書きたい8割、くらいの気持ちで作った
趣味のコードは盆栽
名言だと思った

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">「趣味で書くソースコードは綺麗汚い問わない」との意見が散見されますが、僕は趣味で綺麗なソースコードを追求する人間です。盆栽に近い。</p>&mdash; ミノ駆動 (@MinoDriven) <a href="https://twitter.com/MinoDriven/status/1121262607816544257?ref_src=twsrc%5Etfw">2019年4月25日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## src

<https://github.com/arx-8/swagger-viewer>

## 技術要素

似た技術スタックで Chrome 拡張機能を作りたい人の一助になれば（と盆栽自慢に）少し書く

-   WebExtension Toolbox (Chrome 拡張機能ジェネレーター)
-   TypeScript
-   Jest
-   jsdom
-   React
-   ESLint
-   Clean Architecture

以下盆栽ポイント

-   最新フロントエンド技術 (TypeScript, Jest, typescript-eslint, prettier, etc.) を導入した
-   壊れやすいDOM操作に対してUnitTestを書いた
    -   生DOMは、Jest + jsdom
    -   Reactは、Jest Snapshot test + react-test-renderer
-   WebExtension Toolbox で作った雛形には、TypeScriptはもちろんESLintもテストも入ってないので、自分で追加が必要
-   Clean Architecture
    -   （ゆるく）パッケージ構成をこれでやるとだいたいうまく整理できる気がする
    -   Clean Architecture \* React のベストプラクティスがほしい・・・

### TypeScript導入

下記issueを参考に、
<https://github.com/webextension-toolbox/generator-web-extension/issues/11>
ts-loaderで導入
<https://github.com/arx-8/swagger-viewer/commit/fcf385363729f6fb0a7e39018a5fff2ec5881145>

後に、Jest導入時にうまく動作しなかったため、babelに変更
<https://github.com/arx-8/swagger-viewer/commit/a5dbbe19054189cb03cc88d825ee9c21d2671987>

幸いwebextension-toolboxがbuildに使ってるbabelが7系だっため、TypeScriptのトランスパイルができた
<https://github.com/arx-8/swagger-viewer/blob/master/swagger-viewer/package-lock.json#L9871>

### Jest導入

前述の通り、ts-loaderではうまく動かず、babelに変えた
<https://github.com/arx-8/swagger-viewer/pull/9/files#diff-47c7df0a0b4c5f24af1531f28d7a4d57>

Reactでの css import がエラーになったため、これをやった
<https://jestjs.io/docs/ja/webpack#静的アセットの管理>

### ESLint (+ Prettier)

<https://github.com/arx-8/swagger-viewer/pull/11/files#diff-514b7220e7bfcb11a68634cba4db8961>

-   .eslintrcのextendsが激盛り・・・
-   個人的には、ガチガチのairbnbをプロジェクトに応じて緩めるのが好き

no-restricted-globals, no-restricted-properties で禁止機能を定義している

-   Map は嫌い
    -   [JavaScript で Map オブジェクトを使うのはやめよう - 戦えプログラマン](http://fpg.hateblo.jp/entry/2019/04/14/155041)
-   innerText -> textContent
    -   [DOM の textContent と innerText について - Please Sleep](http://please-sleep.cou929.nu/20110517.html)
    -   「innerHtmlがあるからinnerTextやろ」とやると死ぬ
-   document -> getDocument()
    -   理由は後述

### ESLint + TypeScript + VSCode

VSCode拡張機能のESLintはちゃんとtsファイルにも対応していた
しばらく気付かず悶々としていたが、やはりMSは神

<https://github.com/Microsoft/vscode-eslint/issues/609#issuecomment-460554105>

-   ただデフォルト有効にはならなそうな雰囲気なので、自分で設定が必要

実際の設定はこんな感じ

-   <https://github.com/arx-8/swagger-viewer/blob/aaae948cf150848f44e7a110d3487ce9de328b23/swagger-viewer/.vscode/settings.json>
-   <https://github.com/arx-8/swagger-viewer/blob/aaae948cf150848f44e7a110d3487ce9de328b23/swagger-viewer/.eslintrc.js#L15-L22>

今のところ特にバグにもハマってないので、typescript-eslint は十分プロダクトに投入できるレベルなのでは

### Jest + jsdom

だいたいこんな手順で、documentオブジェクトに任意の値を展開してテストしている

-   1.  getDocument関数をJestでMock化
-   2.  ページ丸ごと文字列としてテストデータとし、それをjsdomに与えてdocumentオブジェクトを生成
-   3.  getDocumentの返値を「2.」にする
-   4.  任意のdocumentオブジェクトに対して、操作が期待通りの状態を作れるかテストする
    -   <https://github.com/arx-8/swagger-viewer/blob/280caa8e155a4e45d68b74c83b532650d531b56b/swagger-viewer/app-src/contentscript/data/DomRepository/index.test.ts>

テスト結果を手で定義するのが面倒なものは、toMatchSnapshotで雑にassert

-   [どのようにreduxのreducerのテストを書くか？ - Qiita](https://qiita.com/akameco/items/31e2078c38ff2fd60213)

no-restricted-globals で document -> getDocument() としてる理由はこれ

-   JestでMockできるのは、外部ファイルからimportしたリソースのみらしい
-   なので、documentオブジェクトにアクセスする場合は、必ずgetDocument（モック化できるfunction）を経由させている

### Jest + react-test-renderer

enzyme より react-test-renderer の方がいいぞ、という記事を読んだのでそうした

-   [Testing React 16.3+ Components with react-test-renderer (without Enzyme)](https://itnext.io/testing-react-16-3-components-with-react-test-renderer-without-enzyme-d9c65d689e88)

コード例

-   <https://github.com/arx-8/swagger-viewer/blob/280caa8e155a4e45d68b74c83b532650d531b56b/swagger-viewer/app-src/contentscript/presentation/App/index.test.tsx>
-   出力内容を1行ずつ見てはいられないが、「これで間違いはない」という状態で一発Snapshot取っておく
-   UI系のライブラリも丸ごとSnapshot取っておくと、バージョンアップの時に動作の保証だけでなく、「どこが変更されたか」がdiffでわかって便利

DOM丸ごと生成したりライブラリのrender結果を丸ごとSnapshot取ったり力技なUTだが、「規模が小さいし実行してほっときゃ終わるだろ」ということで富豪チックに書いた
これでGitHubのhtmlが変更されたとしても、UT回して検知・修正ができるであろうと期待

### 未解決の課題

#### ReactTestUtils.Simulate.click ができない

やり方を知ってる方いたら、ご教授願いたい・・・
公式には記述があるのだが、ver違いなのかReactTestUtils.Simulateがundefinedだった
<https://reactjs.org/docs/test-utils.html#simulate>

「Reactコンポーネントが、ある箇所をクリックされた結果どうなってるか」のSnapshot testができていないのが心残り

# まとめ

-   Chrome拡張機能、型・テストともガッチガチに作れて楽しかったのでオススメ！！