정보
qa-testing 스킬은 배포 후 및 출시 전 검증을 위해 구성 가능한 깊이 수준(스모크, 스탠다드, 풀)에서 웹 페이지나 사이트에 대한 자동화된 점검을 실행합니다. 이 스킬은 렌더링 문제, 깨진 링크, 표면 수준의 접근성, 성능 및 SEO 신호를 신속하게 테스트합니다. 심층 감사나 코드 디버깅의 경우에는 보다 전문적인 스킬로 넘깁니다.
빠른 설치
Claude Code
추천npx skills add rampstackco/claude-skills -a claude-code/plugin add https://github.com/rampstackco/claude-skillsgit clone https://github.com/rampstackco/claude-skills.git ~/.claude/skills/qa-testingClaude Code에서 이 명령을 복사하여 붙여넣어 스킬을 설치하세요
문서
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
altattribute. An emptyalt=""on a decorative image is correct markup and passes; only an absent attribute fails. - Zero broken images
- Every schema block parses (no
invalidentries 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"haverel="noopener" - Page language declared (
langattribute 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);
});
External link audit
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
- Pick the tier. Smoke for routine deploys. Standard for new work. Full for releases.
- Run the snippet. Paste the appropriate console snippet, review output.
- Note failures. Each failure either gets fixed before ship or filed as a known issue.
- 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.
- For Full tier, add: cross-browser testing, Lighthouse audit, schema validation, security headers, 404 handling.
- Document. Use the template in
references/qa-report-template.mdfor 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
references/qa-report-template.md- Markdown report template for standard and full audits.
GitHub 저장소
자주 묻는 질문
qa-testing Skill이란 무엇인가요?
qa-testing은(는) rampstackco이(가) 만든 Claude Skill입니다. Skill은 Claude가 필요할 때 불러오는 지침과 리소스를 묶어 추가 프롬프트 없이 qa-testing 관련 작업을 수행할 수 있게 합니다.
qa-testing은(는) 어떻게 설치하나요?
이 페이지의 설치 명령을 사용하세요. qa-testing을(를) Claude Code 플러그인으로 추가하거나 저장소를 skills 디렉터리에 복제한 다음 Claude를 다시 시작해 Skill을 불러옵니다.
qa-testing은(는) 어떤 카테고리에 속하나요?
qa-testing은(는) 메타 카테고리에 속합니다.
qa-testing은(는) 무료로 사용할 수 있나요?
네. qa-testing은(는) AIMCP에 등록되어 있으며 무료로 설치할 수 있습니다.
연관 스킬
이 스킬은 콘텐츠 콜렉션(Content Collections)을 위한 프로덕션 검증된 설정을 제공합니다. 콘텐츠 콜렉션은 Markdown/MDX 파일을 Zod 검증이 포함된 타입 안전한 데이터 콜렉션으로 변환해주는 TypeScript 최우선 도구입니다. 블로그, 문서 사이트 또는 콘텐츠 중심의 Vite + React 애플리케이션을 구축할 때 타입 안전성과 자동 콘텐츠 검증을 보장하기 위해 사용하세요. Vite 플러그인 구성과 MDX 컴파일부터 배포 최적화 및 스키마 검증에 이르기까지 모든 것을 다룹니다.
이 스킬은 개발자들이 Polymarket 예측 시장 플랫폼을 활용한 애플리케이션을 구축할 수 있도록 지원하며, 거래 및 시장 데이터를 위한 API 통합 기능을 포함합니다. 또한 WebSocket을 통한 실시간 데이터 스트리밍을 제공하여 실시간 거래와 시장 활동을 모니터링할 수 있습니다. 이를 통해 거래 전략을 구현하거나 실시간 시장 업데이트를 처리하는 도구를 생성하는 데 활용할 수 있습니다.
이 스킬은 개발자들이 명령어, 파일, LSP 작업 등 25개 이상의 이벤트 유형에 연결되는 OpenCode 플러그인을 만들 수 있도록 돕습니다. JavaScript/TypeScript 모듈을 위한 플러그인 구조, 이벤트 API 명세, 구현 패턴을 제공합니다. OpenCode AI 어시스턴트의 라이프사이클을 사용자 정의 이벤트 기반 로직으로 가로채거나, 모니터링하거나, 확장해야 할 때 사용하세요.
SGLang은 RadixAttention 프리픽스 캐싱을 활용하여 JSON, 정규식, 에이전트 워크플로우를 위한 고속 구조화 생성에 특화된 고성능 LLM 서빙 프레임워크입니다. 특히 반복되는 프리픽스가 있는 작업에서 상당히 빠른 추론 속도를 제공하여 복잡한 구조화 출력 및 다중 턴 대화에 이상적입니다. 제약 디코딩이 필요하거나 광범위한 프리픽스 공유가 있는 애플리케이션을 구축할 때는 vLLM과 같은 대안보다 SGLang을 선택하십시오.
