Chrome extension form validation evidence is useful when a bug report needs more than “the form did not work.” Login, checkout, signup, and profile forms often fail because of a specific required field, pattern rule, hidden validation message, disabled submit button, or missing inline error. A small Manifest V3 helper can capture the page URL, invalid fields, visible messages, and tester notes so QA teams attach cleaner evidence.
This tutorial shows a practical workflow for QA engineers and SDETs. The extension is intentionally small: the tester clicks the extension, the extension inspects the current form state, and the tester reviews the evidence before attaching it to a bug. It does not claim to read every framework-specific internal validation rule. It captures browser-observable evidence that a human tester can verify.
What the Extension Should Capture
For form bugs, developers usually need the page, test data shape, invalid field, validation message, and reproduction context. A useful evidence object can include:
- Current page URL and title.
- Timestamp and viewport size.
- Form index or stable form identifier when available.
- Field label, name, id, type, required state, and validity state.
- Browser validation message from
validationMessagewhen available. - Visible nearby error text found in the DOM.
- Tester notes for expected result, actual result, role, test data, and environment.
Keep the scope narrow. The extension should collect enough evidence to make the bug reproducible, not silently export every input value from the page.
Why Manifest V3 Works for This QA Use Case
Chrome extension documentation describes a Manifest V3 extension as a package with a manifest.json file plus extension contexts such as service workers, content scripts, extension pages, actions, and optional side panels. For this workflow, the action button gives the tester an explicit capture step. The activeTab permission gives temporary access to the active tab after a user gesture, which fits a tester-controlled evidence capture flow.
Content scripts run in an isolated world, but they can inspect the page DOM. That makes them suitable for collecting labels, required states, validity states, and visible validation text. The scripting API can inject a function or file into the current tab. The storage API persists JSON-serializable values asynchronously across extension contexts, which is useful for saved tester notes. A side panel can host persistent extension UI beside the page if your team wants a richer evidence-review screen.
Starter Extension Structure
Create a small folder for the extension:
form-validation-evidence/
manifest.json
popup.html
popup.js
manifest.json
{
"manifest_version": 3,
"name": "QA Form Evidence",
"version": "0.1.0",
"description": "Captures form-validation evidence for QA bug reports.",
"action": {
"default_popup": "popup.html"
},
"permissions": ["activeTab", "scripting", "storage"]
}
popup.html
<main>
<h1>Form Evidence</h1>
<textarea id="notes" placeholder="Expected result, actual result, role, test data"></textarea>
<button id="capture">Capture</button>
<button id="copy">Copy JSON</button>
<pre id="preview"></pre>
<script src="popup.js"></script>
</main>
Capture Invalid Field Evidence
The popup injects a function into the active tab. That function inspects forms, finds invalid controls, captures visible labels and messages, and returns a compact evidence object.
const notes = document.querySelector("#notes");
const preview = document.querySelector("#preview");
const captureButton = document.querySelector("#capture");
const copyButton = document.querySelector("#copy");
let latestEvidence = null;
chrome.storage.local.get("qaFormNotes", (data) => {
notes.value = data.qaFormNotes || "";
});
notes.addEventListener("input", () => {
chrome.storage.local.set({ qaFormNotes: notes.value });
});
function collectFormEvidence() {
function labelFor(control) {
if (control.id) {
const explicit = document.querySelector(`label[for="${CSS.escape(control.id)}"]`);
if (explicit) return explicit.innerText.trim();
}
const wrapped = control.closest("label");
if (wrapped) return wrapped.innerText.trim();
return control.getAttribute("aria-label") || control.name || control.id || "unlabeled field";
}
function nearbyError(control) {
const describedBy = control.getAttribute("aria-describedby");
if (describedBy) {
const text = describedBy
.split(/\s+/)
.map((id) => document.getElementById(id)?.innerText.trim())
.filter(Boolean)
.join(" | ");
if (text) return text;
}
const container = control.closest(".field, .form-group, .input-group, label, div");
return container?.querySelector('[role="alert"], .error, .invalid-feedback, [data-error]')?.innerText.trim() || "";
}
const controls = [...document.querySelectorAll("input, select, textarea")];
const invalidFields = controls
.filter((control) => typeof control.checkValidity === "function" && !control.checkValidity())
.map((control) => ({
label: labelFor(control),
name: control.name || "",
id: control.id || "",
type: control.type || control.tagName.toLowerCase(),
required: Boolean(control.required),
valuePresent: Boolean(control.value),
validationMessage: control.validationMessage || "",
visibleErrorText: nearbyError(control),
validity: {
valueMissing: control.validity.valueMissing,
typeMismatch: control.validity.typeMismatch,
patternMismatch: control.validity.patternMismatch,
tooShort: control.validity.tooShort,
tooLong: control.validity.tooLong,
rangeUnderflow: control.validity.rangeUnderflow,
rangeOverflow: control.validity.rangeOverflow,
customError: control.validity.customError
}
}));
return {
page: {
url: location.href,
title: document.title,
capturedAt: new Date().toISOString(),
viewport: { width: innerWidth, height: innerHeight }
},
summary: {
totalControls: controls.length,
invalidControls: invalidFields.length
},
invalidFields
};
}
captureButton.addEventListener("click", async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const [{ result }] = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: collectFormEvidence
});
latestEvidence = {
...result,
testerNotes: notes.value
};
preview.textContent = JSON.stringify(latestEvidence, null, 2);
});
copyButton.addEventListener("click", async () => {
if (!latestEvidence) return;
await navigator.clipboard.writeText(JSON.stringify(latestEvidence, null, 2));
});
Step-by-Step QA Workflow
1. Load the unpacked extension. Open chrome://extensions, enable Developer mode, and load the extension folder. Use a test browser profile when possible.
2. Reproduce the form issue. Navigate to the form, enter the same kind of test data used during the failure, and trigger validation by submitting or blurring the relevant field.
3. Capture evidence after the validation state appears. Click the extension action only after inline errors, disabled submit behavior, or native validation messages are visible.
4. Add tester notes. Include role, environment, browser, expected result, actual result, and any important data category. Do not paste passwords, tokens, payment data, or customer data.
5. Review and attach. Copy the JSON, remove anything sensitive, and attach it to the bug report with normal reproduction steps and screenshots.
What to Put in the Bug Report
The evidence JSON should support the human report, not replace it. A strong bug report still needs steps to reproduce, expected result, actual result, environment, browser version, build or release, and screenshot evidence. The extension output helps developers see exactly which field failed validation and which message was shown at capture time.
For example, a checkout bug might say: “Shipping ZIP accepts letters, but submit later fails with a generic address error.” The attached evidence should show the page URL, the ZIP field label, whether the field was required, the browser validity flags, and any visible error message near the field.
Common Mistakes
Capturing before validation appears. If the tester captures too early, the extension may report zero invalid fields. Trigger validation first.
Exporting raw field values. The starter snippet records whether a value is present, not the full value. That is usually safer for bug reports.
Assuming all frameworks expose the same messages. Some apps render validation text in custom components. Keep the selector list adaptable and verify the output manually.
Trusting the evidence without a screenshot. Pair the JSON with a screenshot so developers can compare the captured field state with what the tester saw.
Screenshot Checklist
chrome://extensionsshowing the unpacked QA Form Evidence extension.- The tested form with validation messages visible.
- The extension popup after clicking Capture.
- The generated JSON preview showing invalid field details.
- The final bug report with reproduction steps, screenshot, and attached evidence.
Privacy and Review Guidelines
Form evidence can contain sensitive context even when field values are not exported. URLs, labels, messages, and notes may reveal internal workflows or customer categories. Keep the extension internal, document redaction rules, and avoid capturing raw values unless your team has a clear policy. If raw values are required for a test-data-only environment, mark that behavior clearly in the extension UI.
FAQ
Can a Chrome extension capture every form-validation rule?
No. It can capture browser-observable DOM state, validity flags, and visible messages. Framework-specific internal validation state may not be accessible or reliable.
Why use activeTab for this workflow?
The tester clicks the extension to capture evidence, and activeTab gives temporary access to the active tab after that user gesture. That matches a controlled QA evidence flow.
Should the extension export field values?
Usually no. For most bug reports, recording whether a value is present is safer than exporting the full value. Use raw values only in approved test environments.
Can this replace manual reproduction steps?
No. It improves evidence quality, but the bug report still needs clear steps, expected result, actual result, and screenshots.
Conclusion
Chrome extension form validation evidence gives QA teams a practical way to capture the exact form state behind a validation bug. Keep the extension small, tester-invoked, and reviewable. Capture invalid fields, visible messages, URL metadata, and notes; then attach the evidence beside normal reproduction steps and screenshots.
References
- Chrome Extensions get started guide
- Chrome activeTab documentation
- Chrome content scripts documentation
- Chrome scripting API
- Chrome storage API
- Chrome side panel API
