mcpField
Q
qa-testing

qa-testing

github.com/rampstackcoテスト・品質1.0k284 回インストール1 週間前に更新
リポジトリ

qa-testing について

リリース前後にページ、機能、サイトを確認するための、技術スタックに依存しないQAガイドです。ブラウザーのコンソール用スニペットを使い、スモークテストから包括的なリリース確認まで3段階で実施します。

主な機能

  1. 013段階のQAチェック
  2. 02ブラウザーコンソール用スニペット
  3. 03メタデータとページ構造の確認
  4. 04基本的なアクセシビリティとレスポンシブ確認
  5. 05クロスブラウザーとリリース確認
  6. 06Markdown監査レポートのテンプレート

活用シーン

  1. 01デプロイ後のスモークテスト
  2. 02新しいページや機能の確認
  3. 03大型リリース前のサイト検証
  4. 04ページに関する報告の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rampstackco/claude-skills --skill qa-testing

インストール後、Claude Code で/qa-testingを入力して読み込まれたか確認します。

同梱ファイルREADME.mdreferences/qa-report-template.md

詳細

QA テスト

ページ、機能、またはサイトが正常に動作していることを確認してから、リリース済みと判断してください。技術スタックを問いません。素早く確認できるよう、コンソールスニペットを使います。

このスキルは、WCAGをより深く掘り下げる accessibility-audit や、Core Web Vitalsをより深く掘り下げる performance-optimization よりも高速です。一般的なQAにはこのスキルを使用してください。詳細な監査には、それぞれの専門スキルを使用してください。


使用するタイミング

  • デプロイのたび(スモークテスト)
  • 新しいページや機能の公開後(標準監査)
  • メジャーリリースの前(完全なリリースマトリックス)
  • 「何かがおかしいようだ」という報告の調査
  • サイトやセクションの公開前確認

使用しないタイミング

  • アクセシビリティ準拠の詳細な作業(accessibility-audit を使用)
  • パフォーマンスの詳細な調査(performance-optimization を使用)
  • コードレビューやデバッグ(code-review-web を使用)
  • サイトの初期設定やテクニカルSEOのベースライン確認(seo-technical を使用)

必要な入力

  • テスト対象のページURLまたはサイト
  • 必要なQAのティア(スモーク、標準、または完全)
  • ブラウザーの開発者ツールへのアクセス
  • 標準ティア以外に確認すべき具体的な懸念事項

フレームワーク:3つのティア

QAの範囲は重要度に応じて調整します。状況に合ったティアを選んでください。

ティア実施タイミング所要時間対象範囲
スモークデプロイのたび2分重要なシグナルのみ
標準新しいページや機能10分ページ上の基本事項、アクセシビリティ、構造
完全メジャーリリース、公開前30分以上すべての観点を包括的に確認
ティア1:スモークテスト

「デプロイで明らかな問題が起きていないか」を2分で確認します。デプロイのたびに実施してください。

コンソールスニペット(ブラウザーの開発者ツールに貼り付けます):

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));

合格基準:

  • タイトルが存在し、30〜60文字である
  • canonical が本番ドメインを指している(ステージングやプレビューのURLは不可)
  • H1が1つだけ存在する
  • alt 属性のない画像が0件である。装飾画像の空の alt="" は正しいマークアップとして合格します。失敗となるのは、属性自体がない場合のみです。
  • 壊れた画像が0件である
  • すべてのスキーマブロックを解析できる(スニペットの出力に invalid の項目がない)。そのページに適切な種類のスキーマかどうかは、Rich Results Testを使って完全ティアで確認します。

いずれかの基準を満たさない場合は、スモークテストの問題を修正するまで、より詳細なテストに進まないでください。

ティア2:標準ページ監査

新しいページや機能を対象とした10分間の監査です。ページ上の基本事項に加え、アクセシビリティと構造を確認します。

コンソールスニペット:

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));

合格基準(スモークテストに加えて):

  • メタディスクリプションが120〜160文字である
  • og:image、og:title、twitter:card が存在する
  • H2が存在し、内容がわかりやすい
  • target="_blank" を持つすべての外部リンクに rel="noopener" が指定されている
  • ページの言語が宣言されている(<html> の lang 属性)
  • ファビコンが存在する
ティア 3: フルリリースマトリクス

ローンチ前 30 分のチェックです。すべての観点を網羅します。

観点合格基準
スモークおよび標準すべて合格
アクセシビリティ (基本)ブラウザ監査ツール (例: Lighthouse) を実行し、スコア 90 以上
パフォーマンス (基本)レポートテンプレートのパフォーマンスチェックリストのすべてのしきい値 (INP を含む)
モバイル対応レポートテンプレートのレスポンシブチェックリストのすべてのビューポート
クロスブラウザChrome、Safari、Firefox (関連するオーディエンスがいる場合は Edge も) でテスト済み
フォームすべてのフォームが正常に送信され、正しく検証される
内部リンク壊れた内部リンクがない (ランダムに 20 件サンプリング)
外部リンクすべて 200 を返す (10 件サンプリング)
サイトマップ200 を返し、正規 URL のみをリストする
robots.txt本番クローラーを許可し、該当する場合はステージングをブロックする
セキュリティヘッダーHSTS、X-Frame-Options、X-Content-Type-Options が存在する
HTTPSすべてのリソースが HTTPS 経由で読み込まれ、混合コンテンツがない
404 処理404 ページが HTTP 404 を返す (ソフト 200 ではない)
スキーマ検証すべてのスキーマがリッチリザルトテストで検証される
アナリティクス主要なユーザーアクションでイベントが期待どおりに発火する
キャッシュ動作ページタイプに適したキャッシュヘッダー

ヘッダーについては、次を実行します:

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));
  });

探す項目: strict-transport-security、x-frame-options、x-content-type-options。


特定の QA スニペット

画像監査
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,
});
見出し階層チェック
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');
コントラストのスポットチェック
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
フォーム監査 (フォームごと)
[...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`);
}

ワークフロー

  1. ティアを選びます。 ルーチンデプロイにはスモーク。新しい作業には標準。リリースにはフル。
  2. スニペットを実行します。 適切なコンソールスニペットを貼り付け、出力を確認します。
  3. 失敗を記録します。 各失敗は、出荷前に修正するか、既知の問題として起票します。
  4. 標準ティアの場合、次を追加します: 375px、768px、1440px でのビジュアルレビュー。標準は意図的に 1024px をスキップします。フルティアがテンプレートのレスポンシブチェックリストの残りとともにそれを拾います。主要なユーザーフローをテストします。
  5. フルティアの場合、次を追加します: クロスブラウザテスト、Lighthouse 監査、スキーマ検証、セキュリティヘッダー、404 処理。
  6. 文書化します。 完全な監査には references/qa-report-template.md のテンプレートを使用します。

失敗パターン

  • 「小さな」デプロイでスモークテストをスキップする。 本番障害の半分は、「安全に見えた」デプロイから始まります。
  • スニペットを実行するが、出力を読まない。 コンソールスニペットはツールです。判断とは、それが返すものを読むことです。
  • ビジュアルのみの QA。 ページを目視で確認するだけでは、alt テキストの欠落、壊れたスキーマ、canonical の欠落を見逃します。常にスニペットを実行してください。
  • 単一ブラウザでのテスト。 モバイル Safari と Chrome は、驚くほど異なります。少なくとも Chrome と Safari でテストしてください。
  • モバイル QA なし。 375px のビューポートは、ほとんどのユーザーがいる場所です。そこでテストするか、後で痛い目を見るかです。
  • 修正なしの合否判定。 失敗した QA は、修正または既知の問題チケットを生み出さなければなりません。修正されないまま出荷される失敗した QA は、プロセスの見せかけです。

出力形式

スモークテストの場合: コンソール出力がレポートです。

標準およびフル監査の場合: qa-report-[date].md の Markdown レポート。テンプレートは references/qa-report-template.md を使用します。


必要なデータが利用できない場合

このスキルの出力は、それ自体では生成できないデータ、測定値、またはツールの結果に依存します。必要な入力、ツール、またはデータソースが利用できない、または検証できない場合、認められる出力は、ギャップを明記した成果物です: 何が必要だったか、実際に何が得られたかまたは検証されたか、出力のどの部分が影響を受けるか。成果物を完成させるために必要な数値を捏造、推定、または補間することは決して認められません。明記されたギャップは完全な回答です。


参照ファイル

コメント