フォームの動作確認をしていると、毎回同じ入力を手で行うのが地味に面倒です。
そこで、DevTools(コンソール)に貼り付けるだけで フォームをまとめて自動入力 できるスニペットを用意しておくと、テストの速度が一気に上がります。
この記事では、inputtextareaselect(radio/checkbox含む)を対象に、値のセットだけでなくinput/changeイベントも発火する「実務テスト向け」スニペットをまとめます。(React/Vueなどで「値を入れただけだとUIが反応しない」ケースの対策にもなります)
この記事でわかること
- フォーム自動入力スニペットでできること(select/radio/checkbox含む)
- DevToolsに貼り付けて使う手順と、再実行・オプションの使い方
- password / email / date / tel / number などtype別入力ルールのカスタマイズ方法
このスニペットでできること
対象要素
input/textarea/selectを対象- 表示されている要素だけを入力対象にする(
display:noneなどは除外)
入力ルール(type別)
- text/textarea:
test1,test2…のように連番で入力 - password:
password - email:
test@example.com - date:今日の日付(
YYYY-MM-DD) - tel:
09012345678 - number:
0
select / radio / checkbox の扱い
- select:無効ではない最後のoptionを選択
- radio:同じ
nameのグループ内で最後の要素だけを選択(他は解除) - checkbox:同じ
nameのグループ内で最後の要素だけをON(他はOFF)
input/changeイベントを発火する理由
単純にel.value = '...'で値を入れるだけだと、UI側(React/Vue等)が監視しているイベントが発火せず、「画面の表示」や「バリデーション」が更新されないことがあります。
このスニペットはinputとchangeを両方発火することで、入力として扱われやすい形にしています。
DOMが増えるフォームにも対応(複数パス実行)
ステップフォームや条件分岐で、入力をきっかけに次の入力欄がDOMに追加されるケースがあります。
そのため、このスニペットでは「入力 → DOMの更新が落ち着くまで待機 → 再度入力」を複数回繰り返し、途中で追加された要素もまとめて埋められるようにしています。
使い方
DevToolsに貼り付けて実行
- ChromeなどのDevToolsを開く
- Consoleにスニペットを貼り付けて実行
- 完了するとログが出る(例:
[fillInputsSmart] done:)
再実行(window.fillInputsSmart)
実行するとwindow.fillInputsSmartが利用できるようになるので、必要に応じて手動で再実行できます。
window.fillInputsSmart();
オプション(maxPass / idleMs / timeoutMs / textStart)
DOMの追加が多いページや、待ち時間を調整したいときに使います。
window.fillInputsSmart({
maxPass: 10,
idleMs: 200,
timeoutMs: 2000,
textStart: 1
});
スニペット(コピペ用)
Copyをクリックするとコピーできます。
(() => { // 値をセットしただけではUIが反応しないケースがあるため、 // input/change を両方発火して「ユーザー入力に近い挙動」に寄せる const dispatchInputEvents = (el) => { el.dispatchEvent(new Event('input', { bubbles: true })); el.dispatchEvent(new Event('change', { bubbles: true })); }; // input/textarea に値をセットしてイベントを発火する // disabled/readOnly は対象外、同じ値なら更新しない(無駄なイベント発火を避ける) const setValue = (el, value) => { if (!el || el.disabled || el.readOnly) { return false; } if (el.value === value) { return false; } // focus しておくと、フォーカス依存のUI/バリデーションが動く場合がある el.focus(); el.value = value; dispatchInputEvents(el); return true; }; // checkbox/radio の checked を切り替えてイベントを発火する // disabled は対象外、同じ状態なら更新しない const setChecked = (el, checked) => { if (!el || el.disabled) { return false; } if (el.checked === checked) { return false; } el.focus(); el.checked = checked; dispatchInputEvents(el); return true; }; // select は「無効ではない最後の option」を選択する(テスト用の簡易ルール) const selectLastOption = (selectEl) => { if (!selectEl || selectEl.disabled) { return false; } // disabled な option を除外して選択肢を作る const options = Array.from(selectEl.options).filter((opt) => !opt.disabled); if (options.length === 0) { return false; } const last = options[options.length - 1]; if (selectEl.value === last.value) { return false; } selectEl.value = last.value; dispatchInputEvents(selectEl); return true; }; // 非表示要素を除外して、画面上で操作できる要素だけを対象にする // ※ display:none / visibility:hidden / opacity:0 を弾き、サイズ0も弾く const isVisible = (el) => { const style = window.getComputedStyle(el); if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') { return false; } const rect = el.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; }; // DOMが「一定時間」変化しなくなるまで待つ // 入力によって次の項目が表示される(DOM追加される)フォームに追従するため const waitForDomIdle = ({ idleMs = 120, timeoutMs = 1000 } = {}) => { return new Promise((resolve) => { let idleTimer = null; let timeoutTimer = null; let settled = false; const finish = (observer) => { if (settled) { return; } settled = true; if (idleTimer) { clearTimeout(idleTimer); } if (timeoutTimer) { clearTimeout(timeoutTimer); } observer.disconnect(); resolve(); }; // DOMに変化が入るたびに idle 判定をリセットする const scheduleIdleFinish = (observer) => { if (idleTimer) { clearTimeout(idleTimer); } idleTimer = setTimeout(() => { finish(observer); }, idleMs); }; const observer = new MutationObserver(() => { scheduleIdleFinish(observer); }); observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true }); // 変化が止まらない場合でも無限待ちしないための保険 timeoutTimer = setTimeout(() => { finish(observer); }, timeoutMs); scheduleIdleFinish(observer); }); }; // text/textarea に入れる連番(test1, test2...)を生成 const createTextCounter = (start = 1) => { let textCounter = start; const next = () => { const v = `test${textCounter}`; textCounter += 1; return v; }; const getCurrent = () => textCounter; return { next, getCurrent }; }; // 同じ要素に対しては、複数パスでも同じ文字列を使い続けるためのキャッシュ // WeakMap なので要素が消えれば自動で解放される const textValueByEl = new WeakMap(); const getOrAssignTextValue = (el, textCounterState) => { const existing = textValueByEl.get(el); if (existing) { return existing; } const v = textCounterState.next(); textValueByEl.set(el, v); return v; }; // input[type="date"] に入れる「今日」を YYYY-MM-DD で作る const getTodayYmd = () => { const d = new Date(); const yyyy = String(d.getFullYear()); const mm = String(d.getMonth() + 1).padStart(2, '0'); const dd = String(d.getDate()).padStart(2, '0'); return `${yyyy}-${mm}-${dd}`; }; // 1回分(1パス)の入力処理 // 先に select/radio/checkbox を処理して、表示条件のトリガーになりやすい入力を先に確定させる // その後に text/textarea 等を埋める const fillInputsPass = ({ textCounterState }) => { let filledCount = 0; const inputsPhase1 = Array.from(document.querySelectorAll('input, select')) .filter((el) => isVisible(el)); // radio/checkbox は name 単位でまとめて処理する const radioGroups = new Map(); const checkboxGroups = new Map(); for (const el of inputsPhase1) { const tag = el.tagName.toLowerCase(); if (tag === 'select') { if (selectLastOption(el)) { filledCount += 1; } continue; } const type = (el.getAttribute('type') || 'text').toLowerCase(); if (type === 'radio') { // name が無いとグループにできないのでスキップ if (!el.name) { continue; } const name = el.name; if (!radioGroups.has(name)) { radioGroups.set(name, []); } radioGroups.get(name).push(el); continue; } if (type === 'checkbox') { // name が無いとグループにできないのでスキップ if (!el.name) { continue; } const name = el.name; if (!checkboxGroups.has(name)) { checkboxGroups.set(name, []); } checkboxGroups.get(name).push(el); continue; } } // radio:同じ name の最後の要素だけON(他はOFF) for (const group of radioGroups.values()) { const last = group[group.length - 1]; let changed = false; for (const el of group) { const ok = setChecked(el, el === last); if (ok) { changed = true; } } // グループ単位で1カウントにする if (changed) { filledCount += 1; } } // checkbox:同じ name の最後の要素だけON(他はOFF) for (const group of checkboxGroups.values()) { const last = group[group.length - 1]; let changed = false; for (const el of group) { const ok = setChecked(el, el === last); if (ok) { changed = true; } } if (changed) { filledCount += 1; } } const inputsPhase2 = Array.from(document.querySelectorAll('input, textarea')) .filter((el) => isVisible(el)); // date 入力用に「今日」を先に計算 const todayYmd = getTodayYmd(); for (const el of inputsPhase2) { const tag = el.tagName.toLowerCase(); if (tag === 'textarea') { // textarea は連番の文字列を入れる const v = getOrAssignTextValue(el, textCounterState); if (setValue(el, v)) { filledCount += 1; } continue; } const type = (el.getAttribute('type') || 'text').toLowerCase(); // radio/checkbox は Phase1 で処理済み if (type === 'radio' || type === 'checkbox') { continue; } // 対象外 if (['button', 'submit', 'reset', 'image', 'file', 'hidden'].includes(type)) { continue; } // email は形式が必要なことが多いので固定値 if (type === 'email') { if (setValue(el, 'test@example.com')) { filledCount += 1; } continue; } // date は YYYY-MM-DD を入れる(今日) if (type === 'date') { if (setValue(el, todayYmd)) { filledCount += 1; } continue; } // tel は電話番号形式を固定で入れる if (type === 'tel') { if (setValue(el, '09012345678')) { filledCount += 1; } continue; } // number は 0 を入れる if (type === 'number') { if (setValue(el, '0')) { filledCount += 1; } continue; } // password は 'password' を固定で入れる // ※ 必要なら 'Password123!' など、要件に合う文字列に変更する if (type === 'password') { if (setValue(el, 'password')) { filledCount += 1; } continue; } // 上記以外は連番で埋める(text/search/url など) const v = getOrAssignTextValue(el, textCounterState); if (setValue(el, v)) { filledCount += 1; } } return { filledCount }; }; // 複数パスで入力を実行する // 1パスで埋めた入力がトリガーになってDOMが増えた場合、次のパスで追加分も埋める // totalFilled が増えなくなったら終了 const fillInputsSmart = async ({ maxPass = 6, idleMs = 120, timeoutMs = 1000, textStart = 1 } = {}) => { const textCounterState = createTextCounter(textStart); let totalFilled = 0; for (let i = 0; i < maxPass; i += 1) { const before = totalFilled; const result = fillInputsPass({ textCounterState }); totalFilled += result.filledCount; // このパスで新規に埋められなければ終了 if (totalFilled === before) { break; } // DOM変化が落ち着くのを待ってから次パスへ await waitForDomIdle({ idleMs, timeoutMs }); } // filledCount: 埋めた数(目安) / nextTextNumber: 次に使う連番(デバッグ用) return { filledCount: totalFilled, nextTextNumber: textCounterState.getCurrent() }; }; // コンソールから再実行できるようにグローバルへ公開 window.fillInputsSmart = fillInputsSmart; // 貼り付けてすぐ実行(コンソール運用前提) (async () => { const result = await window.fillInputsSmart(); console.log('[fillInputsSmart] done:', result); })();})();カスタマイズメモ(よく変える箇所)
telをハイフン付きにする
type === 'tel'の箇所を次のように変更します。
if (type === 'tel') {
if (setValue(el, '090-1234-5678')) {
filledCount += 1;
}
continue;
}
emailの値を変える
if (type === 'email') {
if (setValue(el, 'test@sample.com')) {
filledCount += 1;
}
continue;
}
dateを固定日にする(今日ではなく任意の日付)
todayYmd の代わりに固定文字列(YYYY-MM-DD)を渡します。
if (type === 'date') {
if (setValue(el, '2026-02-11')) {
filledCount += 1;
}
continue;
}
埋めきれない場合(maxPassを増やす)
入力によりDOMが何段階も増える場合、パス回数を増やすと埋められることがあります。
window.fillInputsSmart({
maxPass: 12,
idleMs: 200,
timeoutMs: 2000,
textStart: 1
});
注意点
本番環境・個人情報入力画面で実行しない
自動入力したまま送信してしまう可能性があります。
検証環境・ローカル環境での利用を前提にしています。
対象外のtypeがある
hiddenfilebuttonsubmitなどは対象外にしています。
必要に応じて条件分岐を追加する必要があります。
