site-discovery

Systematically explore and map websites to understand what needs testing.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "site-discovery" with this command: npx skills add cristian-robert/autonomous-automation-tester/cristian-robert-autonomous-automation-tester-site-discovery

Site Discovery

Systematically explore and map websites to understand what needs testing.

CRITICAL: Playwright Session Behavior

⚠️ EACH MCP CALL CREATES A NEW BROWSER SESSION. THE BROWSER CLOSES AFTER EACH CALL.

What This Means for Discovery

❌ WRONG: Call 1: browser_navigate to homepage Call 2: browser_click on category → FAILS (new blank browser!)

✅ CORRECT: Call 1: browser_run_code with navigate + accept cookies + capture snapshot Call 2: browser_run_code with navigate + accept cookies + click category + capture snapshot (must repeat ALL setup steps because session is gone)

The Rule

For any multi-step exploration, use browser_run_code with ALL steps in one script.

If you need to explore a different page or interaction, you MUST start fresh:

  • Navigate to the URL

  • Accept cookies again

  • Perform the interaction

  • Capture snapshot

There is no way to "continue" from a previous session.

Discovery Process

Phase 1: Homepage Analysis

Use a single browser_run_code call to get everything:

python .claude/skills/mcp-client/scripts/mcp_client.py call playwright browser_run_code '{ "code": " await page.goto("https://example.com\");

// Handle cookies
const acceptBtn = page.getByRole(\"button\", { name: /accept|agree|cookie/i });
if (await acceptBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
  await acceptBtn.click();
  await page.waitForTimeout(1000);
}

// Get all navigation links
const navLinks = await page.locator(\"nav a, header a\").evaluateAll(els =>
  els.map(e => ({ text: e.textContent?.trim(), href: e.href }))
);

// Get footer links
const footerLinks = await page.locator(\"footer a\").evaluateAll(els =>
  els.map(e => ({ text: e.textContent?.trim(), href: e.href }))
);

// Get all links for full mapping
const allLinks = await page.getByRole(\"link\").evaluateAll(els =>
  els.slice(0, 50).map(e => ({
    text: e.textContent?.trim()?.substring(0, 50),
    href: e.href
  }))
);

// Get forms (login, search, etc.)
const forms = await page.locator(\"form\").evaluateAll(els =>
  els.map(e => ({
    action: e.action,
    inputs: Array.from(e.querySelectorAll(\"input\")).map(i => ({
      type: i.type,
      name: i.name,
      placeholder: i.placeholder
    }))
  }))
);

// Get accessibility snapshot
const snapshot = await page.accessibility.snapshot();

return JSON.stringify({
  url: page.url(),
  title: await page.title(),
  navLinks,
  footerLinks,
  allLinks,
  forms,
  snapshot
}, null, 2);

" }'

Phase 2: Explore Subpages

For EACH subpage, run a NEW browser_run_code (previous session is closed!):

python .claude/skills/mcp-client/scripts/mcp_client.py call playwright browser_run_code '{ "code": " await page.goto("https://example.com/login\");

// Handle cookies AGAIN (new session!)
const acceptBtn = page.getByRole(\"button\", { name: /accept/i });
if (await acceptBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
  await acceptBtn.click();
  await page.waitForTimeout(500);
}

// Analyze this page
const inputs = await page.locator(\"input\").evaluateAll(els =>
  els.map(e => ({
    type: e.type,
    name: e.name,
    placeholder: e.placeholder,
    testid: e.dataset.testid
  }))
);

const buttons = await page.locator(\"button\").evaluateAll(els =>
  els.map(e => ({
    text: e.textContent?.trim(),
    type: e.type,
    testid: e.dataset.testid
  }))
);

const snapshot = await page.accessibility.snapshot();

return JSON.stringify({
  url: page.url(),
  pageType: \"authentication\",
  inputs,
  buttons,
  snapshot
}, null, 2);

" }'

Phase 3: Explore Interactive Elements

To understand what clicking does (e.g., does it open a submenu or navigate?):

python .claude/skills/mcp-client/scripts/mcp_client.py call playwright browser_run_code '{ "code": " await page.goto("https://example.com\");

// Handle cookies
const acceptBtn = page.getByRole(\"button\", { name: /accept/i });
if (await acceptBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
  await acceptBtn.click();
  await page.waitForTimeout(500);
}

const initialUrl = page.url();

// Click on navigation element
const navItem = page.getByRole(\"link\", { name: /products/i }).first();
await navItem.click();
await page.waitForTimeout(1500);

const afterClickUrl = page.url();
const didNavigate = afterClickUrl !== initialUrl;

// Check if submenu appeared
const submenuVisible = await page.locator(\"[class*=submenu], [class*=dropdown]\").isVisible().catch(() => false);

// Look for \"View all\" type links
const viewAllLinks = await page.getByRole(\"link\").filter({ hasText: /view all|see all|vezi toate/i }).allTextContents();

const snapshot = await page.accessibility.snapshot();

return JSON.stringify({
  initialUrl,
  afterClickUrl,
  didNavigate,
  submenuVisible,
  viewAllLinks,
  snapshot
}, null, 2);

" }'

Phase 4: Discover Behavioral Data (For Negative Tests)

MANDATORY before writing negative tests! You must discover actual error messages, validation text, and UI behavior.

Why This Matters

You cannot assume:

  • What error message appears on failed login

  • How validation errors are displayed

  • What text/class/role error elements have

  • Whether errors appear inline, as toasts, or in alerts

Discover Login Error Behavior

python .claude/skills/mcp-client/scripts/mcp_client.py call playwright browser_run_code '{ "code": " await page.goto("https://example.com/login\");

// Handle cookies
const acceptBtn = page.getByRole(\"button\", { name: /accept/i });
if (await acceptBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
  await acceptBtn.click();
  await page.waitForTimeout(500);
}

// Trigger login failure with invalid credentials
await page.fill(\"input[type=email]\", \"fake_user_12345@nonexistent.com\");
await page.fill(\"input[type=password]\", \"WrongPassword123!\");
await page.click(\"button[type=submit]\");

// Wait for error response
await page.waitForTimeout(3000);

// Capture ALL error-related elements
const errors = await page.locator(\"[class*=error], [class*=Error], [role=alert], [data-testid*=error]\").evaluateAll(els =>
  els.map(e => ({
    text: e.textContent?.trim(),
    className: e.className,
    role: e.getAttribute(\"role\"),
    testid: e.dataset?.testid,
    tagName: e.tagName,
    isVisible: e.offsetParent !== null
  }))
);

// Check for toast/snackbar notifications
const toasts = await page.locator(\"[class*=toast], [class*=notification], [class*=snackbar], [class*=alert]\").evaluateAll(els =>
  els.map(e => ({
    text: e.textContent?.trim(),
    className: e.className,
    isVisible: e.offsetParent !== null
  }))
);

return JSON.stringify({
  url: page.url(),
  errors: errors.filter(e => e.isVisible),
  toasts: toasts.filter(t => t.isVisible)
}, null, 2);

" }'

Discover Form Validation Behavior

python .claude/skills/mcp-client/scripts/mcp_client.py call playwright browser_run_code '{ "code": " await page.goto("https://example.com/login\");

// Handle cookies
const acceptBtn = page.getByRole(\"button\", { name: /accept/i });
if (await acceptBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
  await acceptBtn.click();
}

// Test 1: Empty form submission
await page.click(\"button[type=submit]\");
await page.waitForTimeout(500);

// Check HTML5 validation messages
const emailInput = page.locator(\"input[type=email]\");
const emailValidation = await emailInput.evaluate((el) => ({
  validationMessage: el.validationMessage,
  validity: {
    valueMissing: el.validity.valueMissing,
    typeMismatch: el.validity.typeMismatch
  }
}));

// Test 2: Invalid email format
await page.fill(\"input[type=email]\", \"not-an-email\");
await page.click(\"button[type=submit]\");
await page.waitForTimeout(500);

const emailFormatValidation = await emailInput.evaluate((el) => ({
  validationMessage: el.validationMessage,
  validity: {
    typeMismatch: el.validity.typeMismatch
  }
}));

// Check for custom validation messages near inputs
const customErrors = await page.locator(\"input ~ [class*=error], input + [class*=error], [class*=field-error]\").evaluateAll(els =>
  els.map(e => ({
    text: e.textContent?.trim(),
    className: e.className
  }))
);

return JSON.stringify({
  emptyEmailValidation: emailValidation,
  invalidEmailValidation: emailFormatValidation,
  customErrors
}, null, 2);

" }'

Discover Success State Behavior

python .claude/skills/mcp-client/scripts/mcp_client.py call playwright browser_run_code '{ "code": " // Example: Discover what happens on successful action await page.goto("https://example.com/search\");

// Perform action
await page.fill(\"input[type=search]\", \"test query\");
await page.click(\"button[type=submit]\");

// Wait for navigation or results
await page.waitForLoadState(\"networkidle\");

return JSON.stringify({
  finalUrl: page.url(),
  urlContainsQuery: page.url().includes(\"test\") || page.url().includes(\"q=\"),
  title: await page.title()
}, null, 2);

" }'

Document Discovered Behavior

Add to your site map:

Discovered UI Behaviors

Login Errors

  • Invalid credentials: "Email sau parolă incorectă" (role="alert")
  • Empty email: HTML5 validation "Please fill out this field"
  • Invalid email format: HTML5 validation "Please include an '@' in the email address"

Form Validation

  • Validation appears: inline below input
  • Error class: .error-message
  • Required fields show: HTML5 native validation

Success States

  • Login success: Redirects to /dashboard
  • Search success: URL contains ?q={query}

Page Classification

For each discovered page, classify as:

Type Indicators

Authentication /login, /register, password field

Listing Multiple items, filters, pagination

Detail Single item focus, add to cart

Transaction Cart, checkout, payment

Form Contact, application, feedback

Content Blog, about, FAQ

Dashboard Requires auth, user data

See references/page-classification.md for detailed indicators.

Output Format

Site Map: [Website Name]

Statistics

  • Pages discovered: X
  • Requires auth: Y
  • Critical paths: Z

Pages

Authentication (P0)

URLTypeAuth Required
/loginLoginNo
/registerRegistrationNo

Core Features (P0)

URLTypeAuth Required
/HomepageNo
/productsListingNo
/cartCartNo

User Area (P1)

URLTypeAuth Required
/accountDashboardYes
/ordersOrder HistoryYes

URL Patterns

  • /products/:id - Product detail pages
  • /category/:slug - Category pages

Auth Barriers

  • /account/* requires login
  • /checkout requires login

Navigation Behavior

  • Category links open submenus (click "View all" to navigate)
  • User menu requires hover to reveal

Limits

Scope Max Pages Max Depth

Smoke 20 2

Regression 100 4

Edge Cases

Login wall: Map public pages first, note auth requirement Large site: Focus on main nav, sample from large sections SPA: Watch for URL hash changes, trigger nav via clicks Multi-step nav: Some clicks open submenus, not pages - document the behavior

Remember

Every exploration requires a fresh browser_run_code call that includes:

  • Navigation to URL

  • Cookie acceptance (session is new!)

  • Any interactions needed

  • Snapshot capture

You cannot "continue" from a previous exploration. Start fresh each time.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

automation-tester

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

full-workflow

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

test-generation

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

test-fixer

No summary provided by upstream source.

Repository SourceNeeds Review