質問: link_to '> Home', root_path, class: 'btn btn-outline-primary mb-3 ml-3' ``` #### 「CKEditor RSpec」でググっても私と同じ事象で困っている記事が出てこないためご指摘頂ければ幸いです。 5/29追記 teratailのCKEditor見たらちゃんとtextareaになっている。。。やはりCKEditorの実装に問題があるぽい。質問のタイトルも変えるべきか。。 ![イメージ説明](https://teratail-v2.storage.googleapis.com/uploads/contributed_images/de449ef7079968f1c0d29b90776cd54e.jpeg)
回答: こんにちは。 ちょっと間違っているかもしれませんが、同じようにCKEditorの動きを確認する機会があったので、それを踏まえてコメントしますね。 アプリケーションがRailsの前提でお話します。 ### id="editor" に対しての操作ができない理由 本来のtextareaに対して、確かCKEditorは以下のように働きます。 1. 本来のフォームを描画(RailsのViewでのレンダリング) 2. 非同期であとからCKEditor用のJSの処理が呼び出される (私の手元のではiframeを使っていましたが、そうではないかもしれません) 3. 本来のフォームはdisplat: noneで非表示にする(CKEditor側がそのように変更する) 4. CKEditor側のメニュー・入力フォームを差し替えて有効な状態にする Seleniumは要素が非表示だと、セレクタで指定しようとしても操作ができなくなります。 なので、 はHTML上では存在しているけれど、Seleniumから操作ができないということになります。 一方で、じゃあCKEditorの入力したデータはなぜサーバ側に正しく渡るのかというと、Submitの時にCKEditorのテキストエリアの入力値を、本来の id="editor" のデータに差し替えて送信しているからです。 ### CKEditorのテキストエリアにSelenimで値をセットするにはどうするの? では、CKEditor側のテキストエリアに文字をセットできればきっと大丈夫...というところですが、どう指定するといいのか? いくつか方法があります。 #### (1) Seleniumのレコーディング機能を使って、いったん手動で目的の操作を実行する。 その際に、実行記録がテストケースとして自動で書き出されますので、それをもとにどんな操作をしているのかを判定する。 以下のような感じになります。 !イメージ説明 CKEDitorのテキストエリアに文字を入力している部分は、Selenium IDEのコマンド的には"edit content" で、セレクタは class=.cke_edittable です。 お手元の環境だと、ここがどれに当たるかは若干違うかもしれないので、同じようにレコーディングをしてみてセレクタがどこにあたるかを特定するといいと思います。 同じようなクラスを持った要素がもしたくさんあったとして、できるだけ絞り込めるようにするといいので、おそらく class=.cke_edittable cke_xxxx cke_xxxxxx みたいなものかと思います。 また、レコーディングした処理はエクスポートが可能です。 (今回キャプチャに利用したもの) #### (2) edit content が使えない場合 さて、レコーディングした結果だと、edit content というコマンドを使っているのですが、Capybaraでspecを書く場合、これに対応する操作が見当たらない、実行できないかもしれません。 (細かくAPIドキュメントを読んでいないのですみません) もしうまくいかない場合は、Capybaraの処理の中で、「HTMLの要素に対しいてキーやマウス入力をさせる」のではなくて、「JavaScriptを実行してCKEditorのテキストエリアに値をセットする」という操作で代用できます。 CKEditorは、**CKEDITOR.instances という形でJavaScriptから特定できます**。 テキストエリアであれば、おそらく CKEDITOR.instances.editor に値を設定できればいいと思います。 Exp. CKEDITOR.instances.editor.setData('なにかの値'); Capybaraの処理の中でここをどう書くかですが、**page.execute_script**( ... JavaScript ... ); で実行できると思います。 以下も参考になるかと思います。 長くなりましたが、全く外していましたらご容赦くださいませ...。 ### 追記 #### CKEditor not definedの件 まず、developmentモードではただしくCKEditorが表示される場合。 testモードでCapybaraでのE2Eテストをする際に、ヘッドレスモードにしていますか? もしCIではなくローカル開発環境で実行しているようでしたら、可能だったらブラウザが開いて自動で操作が進むなか、CKEditorが描画されるところまでいっているか眺めてみてください。 描画されない場合:CKEditorの設定を確認 描画される場合:テストをwaitさせてみる 自動でテストの場合はブラウザでの読み込みが完了する前にテストのステップが進行してしまうので、「そんな要素は無いよ」といったエラーになることがあります。 CKEditorは上記の通りAjaxで後追いで読み込まれるので、操作をする少し前に、sleep処理を挟んだりAjax処理が完了するのを待つ処理を入れたりするといいのかもしれません。 CKEditorではないのですが、わたしもAjaxの処理を待つ関係で以下のような処理を入れています。 参考:RSpec+Capybaraでajaxとかcssとかを待つ #### JSを使わない方法 Capybara + CKEditor + RSpecだと情報が少ないかもしれませんが、CKEditor + Selenium だと、もう少し情報が出てきそうです。 ちょっと探したら以下のようなものがありました。 Pythonのコードですが、page.execute_script() ではなく一般的な「HTMLの要素を見つけてそこにテキストをセットする」方法です。 本来はこちらのほうが自然な気はしますね。 やり方としては、Capybaraでの page が現在のメインのページを表しているので、以下の流れになっています。 * 念の為CKEditorが描画されるまで少し待つ(ここでもsleepで待っている感じです) * CKEditorを描画しているiframe側に操作対象を移す * ブラウザで表示の際、CKEditorを描画しているifarmeがありませんか?ターゲットはそこになります * 操作対象をiframeに切り替え、その中の body (CKEditorのテキストエリア部分)を特定する * editor_body.send_keys("Test Body") を実行 * 入力後 iframeから外れる場合は操作対象を切り替え まだ手元では試していないのですが、わたしも知っておいてこれは良さそう!と思いましたので、やってみようと思います。(ソースありがとうございます!) もしよければ試してみてくださいね。