QA testing

Run QA testing on a page, feature, or full site at one of three depth tiers (smoke, standard, full).

QA testing — Creative Direction skill highlight diagram. Navy header card reads 'Impactful Creative Direction' with the subtitle… (from the rampstackco/claude-skills README)

From the rampstackco/claude-skills README — shows the whole collection, not only this skill. · view on GitHub

How to use it

Claude Code
  1. Run the line below. It pulls the whole folder into ~/.claude/skills/qa-testing, including the files SKILL.md points to.
  2. Describe your job in plain words. Claude Code follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit rampstackco/claude-skills/skills/qa-testing#main ~/.claude/skills/qa-testing

For one project only, change the path to .claude/skills/qa-testing. This skill also uses robots.txt — copying SKILL.md alone won't be enough. See the folder on GitHub.

Claude (web or desktop app)
  1. On this page open ⋯ → Download .md.
  2. Save it as SKILL.md in a folder, zip the folder, then Customize → Skills → + → Create skill → Upload a skill.
  3. Pick the file and Save. Claude shows the name and description and runs a security scan.
  4. Check the skill is switched on.
  5. Start a new chat and describe your job in plain words. The AI follows the skill from there.
ChatGPT or another app
  1. ChatGPT: make a Project and paste it into Instructions.
  2. Neither? Paste it at the top of a new chat — it works for that chat.
Not working?
  • Check which app you pasted it into — the steps above name the right one.
  • Some skills need the paid tier of Claude or ChatGPT.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Source of QA testing

Show the full text303 lines
namedescriptioncategorycatalog_summarydisplay_order
qa-testingRun QA testing on a page, feature, or full site at one of three depth tiers (smoke, standard, full). Use this skill whenever the user asks to QA a page or site, run a smoke test after a deploy, verify a page before launch, or run a regression sweep. Triggers on QA, QA sweep, smoke test, regression test, post-deploy check, pre-launch check, verify the deploy, test this page, does the page render, broken link, 404, image not loading. Also triggers proactively after a deploy or a new page launch where verification matters. Covers accessibility, performance, and SEO at the surface-signal level only: deep audits belong to `accessibility-audit`, `performance-optimization`, and `seo-technical`, and code-level debugging to `code-review-web`.qaPre-launch QA, regression testing, cross-browser checks1

QA Testing

Verify that a page, feature, or site is working before declaring it shipped. Stack-agnostic. Console-snippet driven for speed.

This skill is faster than accessibility-audit (which goes deeper on WCAG) and performance-optimization (which goes deeper on Core Web Vitals). Use this skill for general QA. Use the specialists for deep audits.


When to use

  • After every deploy (smoke test)
  • After launching a new page or feature (standard audit)
  • Before a major release (full release matrix)
  • Investigating a "something looks off" report
  • Pre-launch verification of a site or section

When NOT to use

  • Deep accessibility compliance work (use accessibility-audit)
  • Deep performance investigation (use performance-optimization)
  • Code review or debugging (use code-review-web)
  • Initial site setup or technical SEO baseline (use seo-technical)

Required inputs

  • The page URL or site under test
  • The tier of QA needed (smoke, standard, or full)
  • Browser dev tools access
  • Any specific concerns to check beyond the standard tier

The framework: 3 tiers

QA scales with the stakes. Pick the tier that matches the context.

Tier When to run Time Coverage
Smoke After every deploy 2 minutes Critical signals only
Standard New page or feature 10 minutes On-page basics, accessibility, structure
Full Major release, pre-launch 30+ minutes Comprehensive across all dimensions
Tier 1: Smoke test

The 2-minute "did the deploy break anything obvious?" check. Run after every deploy.

Console snippet (paste in browser dev tools):

const smoke = {
  title: document.title,
  titleLen: document.title.length,
  canonical: document.querySelector('link[rel="canonical"]')?.href,
  h1Count: document.querySelectorAll('h1').length,
  missingAlts: [...document.querySelectorAll('img')].filter(i => !i.hasAttribute('alt')).length,
  schema: [...document.querySelectorAll('script[type="application/ld+json"]')]
    .map(s => { try { return JSON.parse(s.innerText)['@type'] } catch(e) { return 'invalid' } }),
  brokenImages: [...document.querySelectorAll('img')].filter(i => !i.complete || i.naturalWidth === 0).length,
};
console.log(JSON.stringify(smoke, null, 2));

Pass criteria:

  • Title exists and is 30 to 60 characters
  • Canonical points at the production domain (never staging or preview URLs)
  • Exactly one H1
  • Zero images missing the alt attribute. An empty alt="" on a decorative image is correct markup and passes; only an absent attribute fails.
  • Zero broken images
  • Every schema block parses (no invalid entries in the snippet output). Whether the types are the right ones for the page is a Full-tier check, against the Rich Results Test.

If any of these fail, do not proceed with deeper testing until the smoke issue is fixed.

Tier 2: Standard page audit

The 10-minute new-page-or-feature audit. Covers the on-page basics plus accessibility and structure.

Console snippet:

const audit = {
  title: document.title,
  titleLen: document.title.length,
  canonical: document.querySelector('link[rel="canonical"]')?.href,
  metaDesc: document.querySelector('meta[name="description"]')?.content,
  metaDescLen: document.querySelector('meta[name="description"]')?.content?.length,
  ogImage: document.querySelector('meta[property="og:image"]')?.content,
  ogTitle: document.querySelector('meta[property="og:title"]')?.content,
  twitterCard: document.querySelector('meta[name="twitter:card"]')?.content,
  h1Count: document.querySelectorAll('h1').length,
  h1Text: document.querySelector('h1')?.innerText,
  h2Count: document.querySelectorAll('h2').length,
  h2s: [...document.querySelectorAll('h2')].map(h => h.innerText.trim().slice(0, 60)),
  totalImages: document.querySelectorAll('img').length,
  missingAlts: [...document.querySelectorAll('img')].filter(i => !i.hasAttribute('alt')).length,
  brokenImages: [...document.querySelectorAll('img')].filter(i => !i.complete || i.naturalWidth === 0).length,
  externalLinksWithoutNoopener: [...document.querySelectorAll('a[target="_blank"]')]
    .filter(a => !a.rel?.includes('noopener')).length,
  schema: [...document.querySelectorAll('script[type="application/ld+json"]')]
    .map(s => {
      try {
        const d = JSON.parse(s.innerText);
        return d['@graph'] ? d['@graph'].map(x => x['@type']) : d['@type'];
      } catch(e) { return 'invalid' }
    }),
  hasSkipLink: [...document.querySelectorAll('a[href^="#"]')].slice(0, 3)
    .some(a => /skip/i.test(a.textContent) && !!document.getElementById(a.getAttribute('href').slice(1))),
  pageLanguage: document.documentElement.lang || 'NOT SET',
  hasFavicon: !!document.querySelector('link[rel*="icon"]'),
};
console.log(JSON.stringify(audit, null, 2));

Pass criteria (in addition to smoke):

  • Meta description: 120 to 160 characters
  • og:image, og:title, twitter:card present
  • H2s present and descriptive
  • All external links with target="_blank" have rel="noopener"
  • Page language declared (lang attribute on <html>)
  • Favicon present
Tier 3: Full release matrix

The 30-minute pre-launch check. Cover all dimensions.

Dimension Pass criteria
Smoke and standard All pass
Accessibility (basic) Run browser audit tool (e.g., Lighthouse), score above 90
Performance (basic) Every threshold in the report template's Performance checklist, INP included
Mobile responsiveness Every viewport in the report template's responsiveness checklist
Cross-browser Tested in Chrome, Safari, Firefox (and Edge if relevant audience)
Forms All forms submit successfully and validate correctly
Internal links No broken internal links (sample 20 random)
External links All return 200 (sample 10)
Sitemap Returns 200, lists canonical URLs only
robots.txt Allows production crawlers, blocks staging if applicable
Security headers HSTS, X-Frame-Options, X-Content-Type-Options present
HTTPS All resources load over HTTPS, no mixed content
404 handling 404 pages return HTTP 404 (not soft 200)
Schema validation All schema validates in Rich Results Test
Analytics Events fire as expected on key user actions
Cache behavior Cache headers appropriate for page type

For headers, run:

fetch(window.location.origin, { method: 'HEAD' })
  .then(r => {
    const headers = {};
    for (const [k, v] of r.headers.entries()) headers[k] = v;
    console.log(JSON.stringify(headers, null, 2));
  });

Look for: strict-transport-security, x-frame-options, x-content-type-options.


Specific QA snippets

Image audit
const imgs = [...document.querySelectorAll('img')].map(i => ({
  src: i.src.split('/').pop().split('?')[0].slice(0, 60),
  alt: i.hasAttribute('alt') ? (i.alt === '' ? 'DECORATIVE (empty alt)' : i.alt) : 'MISSING',
  width: i.naturalWidth,
  height: i.naturalHeight,
  loaded: i.complete && i.naturalWidth > 0,
}));
console.table(imgs);
console.log({
  total: imgs.length,
  broken: imgs.filter(i => !i.loaded).length,
  noAlt: imgs.filter(i => i.alt === 'MISSING').length,
  decorative: imgs.filter(i => i.alt.startsWith('DECORATIVE')).length,
});
Heading hierarchy check
const headings = [...document.querySelectorAll('h1, h2, h3, h4, h5, h6')].map(h => ({
  level: parseInt(h.tagName[1]),
  text: h.innerText.trim().slice(0, 80),
}));
console.table(headings);

// Check for skipped levels
const levels = headings.map(h => h.level);
let skipped = false;
for (let i = 1; i < levels.length; i++) {
  if (levels[i] > levels[i-1] + 1) {
    console.warn(`Skipped from H${levels[i-1]} to H${levels[i]}: "${headings[i].text}"`);
    skipped = true;
  }
}
if (!skipped) console.log('No skipped heading levels');
Contrast spot-check
function contrast(bg, fg) {
  function lum(hex) {
    return [hex.slice(1,3), hex.slice(3,5), hex.slice(5,7)]
      .map(h => parseInt(h, 16) / 255)
      .map(v => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4))
      .reduce((a, v, i) => a + v * [0.2126, 0.7152, 0.0722][i], 0);
  }
  const [l1, l2] = [lum(bg), lum(fg)];
  const r = ((Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05)).toFixed(2);
  return r + ':1 ' + (parseFloat(r) >= 4.5 ? 'PASS body' : parseFloat(r) >= 3 ? 'PASS large only' : 'FAIL');
}

// Examples
contrast('#FFFFFF', '#4B5563'); // body color check
contrast('#FFFFFF', '#9CA3AF'); // verify gray choices
Form audit (per form)
[...document.querySelectorAll('form')].forEach((form, i) => {
  const fields = [...form.querySelectorAll('input, select, textarea')].map(field => ({
    type: field.type || field.tagName.toLowerCase(),
    name: field.name,
    hasLabel: !!form.querySelector(`label[for="${field.id}"]`) || !!field.closest('label'),
    required: field.required,
  }));
  console.log(`Form ${i + 1}:`);
  console.table(fields);
});
const externalLinks = [...document.querySelectorAll('a[href^="http"]')]
  .filter(a => !a.href.includes(window.location.host));
const issues = externalLinks.filter(a =>
  a.target === '_blank' && (!a.rel?.includes('noopener') || !a.rel?.includes('noreferrer'))
);
if (issues.length) {
  console.warn(`${issues.length} external links missing noopener/noreferrer:`);
  issues.forEach(a => console.warn(a.href));
} else {
  console.log(`All ${externalLinks.length} external links properly attributed`);
}

Workflow

  1. Pick the tier. Smoke for routine deploys. Standard for new work. Full for releases.
  2. Run the snippet. Paste the appropriate console snippet, review output.
  3. Note failures. Each failure either gets fixed before ship or filed as a known issue.
  4. For Standard tier, add: visual review at 375px, 768px, and 1440px. Standard deliberately skips 1024px; the Full tier picks it up with the rest of the template's responsiveness checklist. Test the primary user flow.
  5. For Full tier, add: cross-browser testing, Lighthouse audit, schema validation, security headers, 404 handling.
  6. Document. Use the template in references/qa-report-template.md for full audits.

Failure patterns

  • Skipping smoke tests on "small" deploys. Half of broken-production incidents start with a deploy that "looked safe."
  • Running snippets but not reading the output. The console snippet is a tool. The judgment is reading what it returns.
  • Visual-only QA. Eyeballing a page misses missing alt text, broken schema, missing canonical. Always run the snippet.
  • Single-browser testing. Mobile Safari and Chrome differ enough to surprise you. Test at least Chrome and Safari.
  • No mobile QA. The 375px viewport is where most users live. Test there or get burned later.
  • Pass-fail with no remediation. A failed QA must produce a fix or a known-issue ticket. Failed QA that ships unfixed is process theater.

Output format

For smoke tests: console output is the report.

For standard and full audits: a markdown report at qa-report-[date].md. Use the template in references/qa-report-template.md.


If required data is unavailable

This skill's output depends on data, measurements, or tool results it cannot generate on its own. When a required input, tool, or data source is unavailable or unverifiable, the sanctioned output is the deliverable with the gap stated: what was needed, what was actually obtained or verified, and which parts of the output are affected. Fabricating, estimating, or interpolating a required number to complete the deliverable is never sanctioned. A stated gap is a complete answer.


Reference files

1---
2name: qa-testing
3description: "Run QA testing on a page, feature, or full site at one of three depth tiers (smoke, standard, full). Use this skill whenever the user asks to QA a page or site, run a smoke test after a deploy, verify a page before launch, or run a regression sweep. Triggers on QA, QA sweep, smoke test, regression test, post-deploy check, pre-launch check, verify the deploy, test this page, does the page render, broken link, 404, image not loading. Also triggers proactively after a deploy or a new page launch where verification matters. Covers accessibility, performance, and SEO at the surface-signal level only: deep audits belong to `accessibility-audit`, `performance-optimization`, and `seo-technical`, and code-level debugging to `code-review-web`."
4category: qa
5catalog_summary: "Pre-launch QA, regression testing, cross-browser checks"
6display_order: 1
7---
8 
9# QA Testing
10 
11Verify that a page, feature, or site is working before declaring it shipped. Stack-agnostic. Console-snippet driven for speed.
12 
13This skill is faster than `accessibility-audit` (which goes deeper on WCAG) and `performance-optimization` (which goes deeper on Core Web Vitals). Use this skill for general QA. Use the specialists for deep audits.
14 
15---
16 
17## When to use
18 
19- After every deploy (smoke test)
20- After launching a new page or feature (standard audit)
21- Before a major release (full release matrix)
22- Investigating a "something looks off" report
23- Pre-launch verification of a site or section
24 
25## When NOT to use
26 
27- Deep accessibility compliance work (use `accessibility-audit`)
28- Deep performance investigation (use `performance-optimization`)
29- Code review or debugging (use `code-review-web`)
30- Initial site setup or technical SEO baseline (use `seo-technical`)
31 
32---
33 
34## Required inputs
35 
36- The page URL or site under test
37- The tier of QA needed (smoke, standard, or full)
38- Browser dev tools access
39- Any specific concerns to check beyond the standard tier
40 
41---
42 
43## The framework: 3 tiers
44 
45QA scales with the stakes. Pick the tier that matches the context.
46 
47| Tier | When to run | Time | Coverage |
48|---|---|---|---|
49| Smoke | After every deploy | 2 minutes | Critical signals only |
50| Standard | New page or feature | 10 minutes | On-page basics, accessibility, structure |
51| Full | Major release, pre-launch | 30+ minutes | Comprehensive across all dimensions |
52 
53### Tier 1: Smoke test
54 
55The 2-minute "did the deploy break anything obvious?" check. Run after every deploy.
56 
57Console snippet (paste in browser dev tools):
58 
59```javascript
60const smoke = {
61 title: document.title,
62 titleLen: document.title.length,
63 canonical: document.querySelector('link[rel="canonical"]')?.href,
64 h1Count: document.querySelectorAll('h1').length,
65 missingAlts: [...document.querySelectorAll('img')].filter(i => !i.hasAttribute('alt')).length,
66 schema: [...document.querySelectorAll('script[type="application/ld+json"]')]
67 .map(s => { try { return JSON.parse(s.innerText)['@type'] } catch(e) { return 'invalid' } }),
68 brokenImages: [...document.querySelectorAll('img')].filter(i => !i.complete || i.naturalWidth === 0).length,
69};
70console.log(JSON.stringify(smoke, null, 2));
71```
72 
73**Pass criteria:**
74- Title exists and is 30 to 60 characters
75- Canonical points at the production domain (never staging or preview URLs)
76- Exactly one H1
77- Zero images missing the `alt` attribute. An empty `alt=""` on a decorative image is correct markup and passes; only an absent attribute fails.
78- Zero broken images
79- Every schema block parses (no `invalid` entries in the snippet output). Whether the types are the right ones for the page is a Full-tier check, against the Rich Results Test.
80 
81If any of these fail, do not proceed with deeper testing until the smoke issue is fixed.
82 
83### Tier 2: Standard page audit
84 
85The 10-minute new-page-or-feature audit. Covers the on-page basics plus accessibility and structure.
86 
87Console snippet:
88 
89```javascript
90const audit = {
91 title: document.title,
92 titleLen: document.title.length,
93 canonical: document.querySelector('link[rel="canonical"]')?.href,
94 metaDesc: document.querySelector('meta[name="description"]')?.content,
95 metaDescLen: document.querySelector('meta[name="description"]')?.content?.length,
96 ogImage: document.querySelector('meta[property="og:image"]')?.content,
97 ogTitle: document.querySelector('meta[property="og:title"]')?.content,
98 twitterCard: document.querySelector('meta[name="twitter:card"]')?.content,
99 h1Count: document.querySelectorAll('h1').length,
100 h1Text: document.querySelector('h1')?.innerText,
101 h2Count: document.querySelectorAll('h2').length,
102 h2s: [...document.querySelectorAll('h2')].map(h => h.innerText.trim().slice(0, 60)),
103 totalImages: document.querySelectorAll('img').length,
104 missingAlts: [...document.querySelectorAll('img')].filter(i => !i.hasAttribute('alt')).length,
105 brokenImages: [...document.querySelectorAll('img')].filter(i => !i.complete || i.naturalWidth === 0).length,
106 externalLinksWithoutNoopener: [...document.querySelectorAll('a[target="_blank"]')]
107 .filter(a => !a.rel?.includes('noopener')).length,
108 schema: [...document.querySelectorAll('script[type="application/ld+json"]')]
109 .map(s => {
110 try {
111 const d = JSON.parse(s.innerText);
112 return d['@graph'] ? d['@graph'].map(x => x['@type']) : d['@type'];
113 } catch(e) { return 'invalid' }
114 }),
115 hasSkipLink: [...document.querySelectorAll('a[href^="#"]')].slice(0, 3)
116 .some(a => /skip/i.test(a.textContent) && !!document.getElementById(a.getAttribute('href').slice(1))),
117 pageLanguage: document.documentElement.lang || 'NOT SET',
118 hasFavicon: !!document.querySelector('link[rel*="icon"]'),
119};
120console.log(JSON.stringify(audit, null, 2));
121```
122 
123**Pass criteria** (in addition to smoke):
124- Meta description: 120 to 160 characters
125- og:image, og:title, twitter:card present
126- H2s present and descriptive
127- All external links with `target="_blank"` have `rel="noopener"`
128- Page language declared (`lang` attribute on `<html>`)
129- Favicon present
130 
131### Tier 3: Full release matrix
132 
133The 30-minute pre-launch check. Cover all dimensions.
134 
135| Dimension | Pass criteria |
136|---|---|
137| Smoke and standard | All pass |
138| Accessibility (basic) | Run browser audit tool (e.g., Lighthouse), score above 90 |
139| Performance (basic) | Every threshold in the report template's Performance checklist, INP included |
140| Mobile responsiveness | Every viewport in the report template's responsiveness checklist |
141| Cross-browser | Tested in Chrome, Safari, Firefox (and Edge if relevant audience) |
142| Forms | All forms submit successfully and validate correctly |
143| Internal links | No broken internal links (sample 20 random) |
144| External links | All return 200 (sample 10) |
145| Sitemap | Returns 200, lists canonical URLs only |
146| robots.txt | Allows production crawlers, blocks staging if applicable |
147| Security headers | HSTS, X-Frame-Options, X-Content-Type-Options present |
148| HTTPS | All resources load over HTTPS, no mixed content |
149| 404 handling | 404 pages return HTTP 404 (not soft 200) |
150| Schema validation | All schema validates in Rich Results Test |
151| Analytics | Events fire as expected on key user actions |
152| Cache behavior | Cache headers appropriate for page type |
153 
154For headers, run:
155 
156```javascript
157fetch(window.location.origin, { method: 'HEAD' })
158 .then(r => {
159 const headers = {};
160 for (const [k, v] of r.headers.entries()) headers[k] = v;
161 console.log(JSON.stringify(headers, null, 2));
162 });
163```
164 
165Look for: `strict-transport-security`, `x-frame-options`, `x-content-type-options`.
166 
167---
168 
169## Specific QA snippets
170 
171### Image audit
172 
173```javascript
174const imgs = [...document.querySelectorAll('img')].map(i => ({
175 src: i.src.split('/').pop().split('?')[0].slice(0, 60),
176 alt: i.hasAttribute('alt') ? (i.alt === '' ? 'DECORATIVE (empty alt)' : i.alt) : 'MISSING',
177 width: i.naturalWidth,
178 height: i.naturalHeight,
179 loaded: i.complete && i.naturalWidth > 0,
180}));
181console.table(imgs);
182console.log({
183 total: imgs.length,
184 broken: imgs.filter(i => !i.loaded).length,
185 noAlt: imgs.filter(i => i.alt === 'MISSING').length,
186 decorative: imgs.filter(i => i.alt.startsWith('DECORATIVE')).length,
187});
188```
189 
190### Heading hierarchy check
191 
192```javascript
193const headings = [...document.querySelectorAll('h1, h2, h3, h4, h5, h6')].map(h => ({
194 level: parseInt(h.tagName[1]),
195 text: h.innerText.trim().slice(0, 80),
196}));
197console.table(headings);
198 
199// Check for skipped levels
200const levels = headings.map(h => h.level);
201let skipped = false;
202for (let i = 1; i < levels.length; i++) {
203 if (levels[i] > levels[i-1] + 1) {
204 console.warn(`Skipped from H${levels[i-1]} to H${levels[i]}: "${headings[i].text}"`);
205 skipped = true;
206 }
207}
208if (!skipped) console.log('No skipped heading levels');
209```
210 
211### Contrast spot-check
212 
213```javascript
214function contrast(bg, fg) {
215 function lum(hex) {
216 return [hex.slice(1,3), hex.slice(3,5), hex.slice(5,7)]
217 .map(h => parseInt(h, 16) / 255)
218 .map(v => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4))
219 .reduce((a, v, i) => a + v * [0.2126, 0.7152, 0.0722][i], 0);
220 }
221 const [l1, l2] = [lum(bg), lum(fg)];
222 const r = ((Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05)).toFixed(2);
223 return r + ':1 ' + (parseFloat(r) >= 4.5 ? 'PASS body' : parseFloat(r) >= 3 ? 'PASS large only' : 'FAIL');
224}
225 
226// Examples
227contrast('#FFFFFF', '#4B5563'); // body color check
228contrast('#FFFFFF', '#9CA3AF'); // verify gray choices
229```
230 
231### Form audit (per form)
232 
233```javascript
234[...document.querySelectorAll('form')].forEach((form, i) => {
235 const fields = [...form.querySelectorAll('input, select, textarea')].map(field => ({
236 type: field.type || field.tagName.toLowerCase(),
237 name: field.name,
238 hasLabel: !!form.querySelector(`label[for="${field.id}"]`) || !!field.closest('label'),
239 required: field.required,
240 }));
241 console.log(`Form ${i + 1}:`);
242 console.table(fields);
243});
244```
245 
246### External link audit
247 
248```javascript
249const externalLinks = [...document.querySelectorAll('a[href^="http"]')]
250 .filter(a => !a.href.includes(window.location.host));
251const issues = externalLinks.filter(a =>
252 a.target === '_blank' && (!a.rel?.includes('noopener') || !a.rel?.includes('noreferrer'))
253);
254if (issues.length) {
255 console.warn(`${issues.length} external links missing noopener/noreferrer:`);
256 issues.forEach(a => console.warn(a.href));
257} else {
258 console.log(`All ${externalLinks.length} external links properly attributed`);
259}
260```
261 
262---
263 
264## Workflow
265 
2661. **Pick the tier.** Smoke for routine deploys. Standard for new work. Full for releases.
2672. **Run the snippet.** Paste the appropriate console snippet, review output.
2683. **Note failures.** Each failure either gets fixed before ship or filed as a known issue.
2694. **For Standard tier**, add: visual review at 375px, 768px, and 1440px. Standard deliberately skips 1024px; the Full tier picks it up with the rest of the template's responsiveness checklist. Test the primary user flow.
2705. **For Full tier**, add: cross-browser testing, Lighthouse audit, schema validation, security headers, 404 handling.
2716. **Document.** Use the template in [`references/qa-report-template.md`](references/qa-report-template.md) for full audits.
272 
273---
274 
275## Failure patterns
276 
277- **Skipping smoke tests on "small" deploys.** Half of broken-production incidents start with a deploy that "looked safe."
278- **Running snippets but not reading the output.** The console snippet is a tool. The judgment is reading what it returns.
279- **Visual-only QA.** Eyeballing a page misses missing alt text, broken schema, missing canonical. Always run the snippet.
280- **Single-browser testing.** Mobile Safari and Chrome differ enough to surprise you. Test at least Chrome and Safari.
281- **No mobile QA.** The 375px viewport is where most users live. Test there or get burned later.
282- **Pass-fail with no remediation.** A failed QA must produce a fix or a known-issue ticket. Failed QA that ships unfixed is process theater.
283 
284---
285 
286## Output format
287 
288For smoke tests: console output is the report.
289 
290For standard and full audits: a markdown report at `qa-report-[date].md`. Use the template in [`references/qa-report-template.md`](references/qa-report-template.md).
291 
292---
293 
294## If required data is unavailable
295 
296This skill's output depends on data, measurements, or tool results it cannot generate on its own. When a required input, tool, or data source is unavailable or unverifiable, the sanctioned output is the deliverable with the gap stated: what was needed, what was actually obtained or verified, and which parts of the output are affected. Fabricating, estimating, or interpolating a required number to complete the deliverable is never sanctioned. A stated gap is a complete answer.
297 
298---
299 
300## Reference files
301 
302- [`references/qa-report-template.md`](references/qa-report-template.md) - Markdown report template for standard and full audits.
303 

Discussion

Alternatives

Also in SEO & keywordsSee all 364 in Marketing →