Claude Skill

testgen

Generate tests with skill-preloaded routing, framework detection, and auto-TaskCreate. Triggers on: generate tests, write tests, testgen, create test file, add test coverage.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download 0xdarkmatter-claude-mods-skills_testgen-3dfaf0b.zip · 10 KB
Part of 0xdarkmatter/claude-mods — 94 skills

Install

skills CLI npx skills add https://github.com/0xDarkMatter/claude-mods/tree/main/skills/testgen
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install 0xdarkmatter-claude-mods@llmmart
Git git clone https://github.com/0xDarkMatter/claude-mods.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole 0xdarkmatter/claude-mods collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

TestGen Skill - AI Test Generation

Generate comprehensive tests with automatic framework detection, skill-preloaded general-purpose dispatch, and project convention matching.

Architecture

testgen <target> [--type] [--focus] [--depth]
    │
    ├─→ Step 1: Analyze Target
    │     ├─ File exists? → Read and parse
    │     ├─ Function specified? → Extract signature
    │     ├─ Directory? → List source files
    │     └─ Find existing tests (avoid duplicates)
    │
    ├─→ Step 2: Detect Framework (parallel)
    │     ├─ package.json → jest/vitest/mocha/cypress/playwright
    │     ├─ pyproject.toml → pytest/unittest
    │     ├─ go.mod → go test
    │     ├─ Cargo.toml → cargo test
    │     ├─ composer.json → phpunit/pest
    │     └─ Check existing test patterns
    │
    ├─→ Step 3: Load Project Standards
    │     ├─ AGENTS.md, CLAUDE.md conventions
    │     ├─ Existing test file structure
    │     └─ Naming conventions (*.test.ts vs *.spec.ts)
    │
    ├─→ Step 4: Route to Test Generator (general-purpose + skill preload)
    │     ├─ .ts → general-purpose, preload typescript-ops
    │     ├─ .tsx/.jsx → general-purpose, preload react-ops
    │     ├─ .vue → general-purpose, preload vue-ops
    │     ├─ .py → general-purpose, preload python-pytest-ops
    │     ├─ .go → general-purpose, preload go-ops
    │     ├─ .rs → general-purpose, preload rust-ops
    │     ├─ .php → general-purpose, preload laravel-ops
    │     ├─ E2E/Cypress → general-purpose, preload cypress-ops
    │     ├─ Playwright → general-purpose, preload typescript-ops
    │     ├─ Shell/bash → general-purpose, preload bash-ops
    │     ├─ --visual → Chrome DevTools MCP
    │     └─ Multi-file → parallel general-purpose dispatch
    │
    ├─→ Step 5: Generate Tests
    │     ├─ Create test file in correct location
    │     ├─ Follow detected conventions
    │     └─ Include: happy path, edge cases, error handling
    │
    └─→ Step 6: Integration
          ├─ Auto-create task (TaskCreate) for verification
          └─ Suggest: run tests, /review, /save

Execution Steps

Step 1: Analyze Target

# Check if target exists
test -f "$TARGET" && echo "FILE" || test -d "$TARGET" && echo "DIRECTORY"

# For function-specific: extract signature
command -v ast-grep >/dev/null 2>&1 && ast-grep -p "function $FUNCTION_NAME" "$FILE"

# Fallback to ripgrep
rg "(?:function|const|def|public|private)\s+$FUNCTION_NAME" "$FILE" -A 10

Check for existing tests:

fd -e test.ts -e spec.ts -e test.js -e spec.js | rg "$BASENAME"
fd "test_*.py" | rg "$BASENAME"

Step 2: Detect Framework

JavaScript/TypeScript:

cat package.json 2>/dev/null | jq -r '.devDependencies | keys[]' | grep -E 'jest|vitest|mocha|cypress|playwright|@testing-library'

Python:

grep -E "pytest|unittest|nose" pyproject.toml setup.py requirements*.txt 2>/dev/null

Go:

test -f go.mod && echo "go test available"

Rust:

test -f Cargo.toml && echo "cargo test available"

PHP:

cat composer.json 2>/dev/null | jq -r '.["require-dev"] | keys[]' | grep -E 'phpunit|pest|codeception'

Step 3: Load Project Standards

# Claude Code conventions
cat AGENTS.md 2>/dev/null | head -50
cat CLAUDE.md 2>/dev/null | head -50

# Test config files
cat jest.config.* vitest.config.* pytest.ini pyproject.toml 2>/dev/null | head -30

Test location conventions:

# JavaScript
src/utils/helper.ts → src/utils/__tests__/helper.test.ts  # __tests__ folder
                    → src/utils/helper.test.ts            # co-located
                    → tests/utils/helper.test.ts          # separate tests/

# Python
app/utils/helper.py → tests/test_helper.py               # tests/ folder
                    → tests/utils/test_helper.py         # mirror structure

# Go
pkg/auth/token.go → pkg/auth/token_test.go               # co-located (required)

# Rust
src/auth.rs → src/auth.rs (mod tests { ... })            # inline tests
            → tests/auth_test.rs                          # integration tests

Step 4: Route to Test Generator

Dispatch is skills-first: the generic general-purpose subagent preloads the relevant -ops skill before generating tests.

File Pattern Dispatch Preload
*.ts general-purpose skills/typescript-ops/SKILL.md
*.tsx, *.jsx general-purpose skills/react-ops/SKILL.md + skills/typescript-ops/SKILL.md
*.vue general-purpose skills/vue-ops/SKILL.md + skills/typescript-ops/SKILL.md
*.py general-purpose skills/python-pytest-ops/SKILL.md
*.go general-purpose skills/go-ops/SKILL.md
*.rs general-purpose skills/rust-ops/SKILL.md
*.php general-purpose skills/laravel-ops/SKILL.md
*.cy.ts, cypress/* general-purpose skills/cypress-ops/SKILL.md + skills/typescript-ops/SKILL.md
*.spec.ts (Playwright) general-purpose skills/playwright-ops/SKILL.md + skills/typescript-ops/SKILL.md
playwright/*, e2e/* general-purpose skills/playwright-ops/SKILL.md + skills/typescript-ops/SKILL.md
*.sh, *.bash general-purpose skills/bash-ops/SKILL.md
(--visual flag) Chrome DevTools MCP skills/typescript-ops/SKILL.md

Invoke via Task tool:

Task tool with subagent_type: "general-purpose" (or surviving specialist from table)
model: "sonnet"
Prompt includes:
  - Skill preloading (domain knowledge):
    "First, read these files for testing context:
     - Read: skills/security-ops/references/owasp-detailed.md
     - Read: skills/testing-ops/SKILL.md
     - Read: [Preload column for the matched file pattern]"
  - Source file content
  - Function signatures to test
  - Detected framework and conventions
  - Requested test type and focus

Language-specific preloads (append to the preloading section above):

Language Additional Preload Why
Python skills/python-pytest-ops/SKILL.md Fixtures, marks, parametrize, async testing
Go skills/go-ops/SKILL.md Table-driven tests, benchmarks, testify
Rust skills/rust-ops/SKILL.md Property testing, criterion, proptest

Step 5: Generate Tests

Test categories based on --focus:

Focus What to Generate
happy Normal input, expected output
edge Boundary values, empty inputs, nulls
error Invalid inputs, exceptions, error handling
all All of the above (default)

Depth levels:

Depth Coverage
quick Happy path only, 1-2 tests per function
normal Happy + common edge cases (default)
thorough Comprehensive: all paths, mocking, async

Step 6: Integration

Auto-create task:

TaskCreate:
  subject: "Run generated tests for src/auth.ts"
  description: "Verify generated tests pass and review edge cases"
  activeForm: "Running generated tests for auth.ts"

Suggest next steps:

Tests generated: src/auth.test.ts

Next steps:
1. Run tests: npm test src/auth.test.ts
2. Review and refine edge cases
3. Use /save to persist tasks across sessions

Routing Details

TypeScript/JavaScript (preload typescript-ops)

  • Proper type imports
  • Generic type handling
  • Async/await patterns
  • Mock typing

React/JSX (preload react-ops)

  • React Testing Library patterns
  • Component rendering tests
  • Hook testing (renderHook)
  • Accessibility queries (getByRole)

Vue (preload vue-ops)

  • Vue Test Utils patterns
  • Composition API testing
  • Pinia store mocking

Python (preload python-pytest-ops)

  • pytest fixtures
  • Parametrized tests
  • Mock/patch patterns
  • Async test handling

Go (preload go-ops)

  • Table-driven tests ([]struct pattern)
  • testing.T and subtests (t.Run)
  • Testify assertions (when detected)
  • Benchmark functions (testing.B)
  • Parallel tests (t.Parallel())

Rust (preload rust-ops)

  • #[test] attribute functions
  • #[cfg(test)] module organization
  • #[should_panic] for error testing
  • proptest/quickcheck for property testing

PHP/Laravel (preload laravel-ops)

  • PHPUnit/Pest patterns
  • Database transactions
  • Factory usage

E2E/Cypress (preload cypress-ops)

  • Page object patterns
  • Custom commands
  • Network stubbing

Shell/bash (preload bash-ops)

  • bats / shunit2 patterns
  • Stubbing external commands
  • Exit-code and stderr assertions

Playwright (preload typescript-ops)

  • Page object model patterns
  • Locator strategies
  • Visual regression testing

CLI Tool Integration

Tool Purpose Fallback
jq Parse package.json Read tool
rg Find existing tests Grep tool
ast-grep Parse function signatures ripgrep patterns
fd Find test files Glob tool
Chrome DevTools MCP Visual testing (--visual) Playwright/Cypress

Graceful degradation:

command -v jq >/dev/null 2>&1 && cat package.json | jq '.devDependencies' || cat package.json

Reference Files

For framework-specific code examples, see:

  • frameworks.md - Complete test examples for all supported languages
  • visual-testing.md - Chrome DevTools integration for --visual flag

Integration

Command Relationship
/review Review generated tests before committing
/explain Understand complex code before testing
/save Track test coverage goals
Files (claude-mods)
  • assets
    • .gitkeep 0 B · in bundle
  • references
    • .gitkeep 0 B · in bundle
  • scripts
    • .gitkeep 0 B · in bundle
  • frameworks.md 18 KB
    # TestGen Framework Examples
    
    Code examples for each supported testing framework. These are loaded on-demand when the testgen skill detects a specific framework.
    
    ---
    
    ## Jest/Vitest (TypeScript)
    
    ```typescript
    import { describe, it, expect, vi, beforeEach } from 'vitest';
    import { validateToken, TokenError } from '../auth';
    
    describe('validateToken', () => {
      beforeEach(() => {
        vi.clearAllMocks();
      });
    
      describe('happy path', () => {
        it('should return true for valid JWT token', () => {
          const token = 'eyJhbGciOiJIUzI1NiIs...';
          expect(validateToken(token)).toBe(true);
        });
    
        it('should decode payload correctly', () => {
          const token = createTestToken({ userId: 123 });
          const result = validateToken(token);
          expect(result.payload.userId).toBe(123);
        });
      });
    
      describe('edge cases', () => {
        it('should handle empty string', () => {
          expect(validateToken('')).toBe(false);
        });
    
        it('should handle malformed token', () => {
          expect(validateToken('not.a.token')).toBe(false);
        });
    
        it('should handle expired token', () => {
          const expiredToken = createTestToken({ exp: Date.now() - 1000 });
          expect(validateToken(expiredToken)).toBe(false);
        });
      });
    
      describe('error handling', () => {
        it('should throw TokenError for null input', () => {
          expect(() => validateToken(null)).toThrow(TokenError);
        });
    
        it('should throw with descriptive message', () => {
          expect(() => validateToken(null)).toThrow('Token cannot be null');
        });
      });
    });
    ```
    
    ---
    
    ## React Testing Library
    
    ```typescript
    import { render, screen, fireEvent, waitFor } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import { LoginForm } from '../LoginForm';
    
    describe('LoginForm', () => {
      const mockOnSubmit = vi.fn();
    
      beforeEach(() => {
        mockOnSubmit.mockClear();
      });
    
      it('renders email and password fields', () => {
        render(<LoginForm onSubmit={mockOnSubmit} />);
    
        expect(screen.getByRole('textbox', { name: /email/i })).toBeInTheDocument();
        expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
      });
    
      it('submits form with credentials', async () => {
        const user = userEvent.setup();
        render(<LoginForm onSubmit={mockOnSubmit} />);
    
        await user.type(screen.getByRole('textbox', { name: /email/i }), 'test@example.com');
        await user.type(screen.getByLabelText(/password/i), 'password123');
        await user.click(screen.getByRole('button', { name: /submit/i }));
    
        expect(mockOnSubmit).toHaveBeenCalledWith({
          email: 'test@example.com',
          password: 'password123',
        });
      });
    
      it('shows validation error for invalid email', async () => {
        const user = userEvent.setup();
        render(<LoginForm onSubmit={mockOnSubmit} />);
    
        await user.type(screen.getByRole('textbox', { name: /email/i }), 'invalid');
        await user.click(screen.getByRole('button', { name: /submit/i }));
    
        expect(await screen.findByText(/invalid email/i)).toBeInTheDocument();
        expect(mockOnSubmit).not.toHaveBeenCalled();
      });
    
      it('disables submit button while loading', () => {
        render(<LoginForm onSubmit={mockOnSubmit} isLoading />);
    
        expect(screen.getByRole('button', { name: /submit/i })).toBeDisabled();
      });
    });
    ```
    
    ---
    
    ## pytest (Python)
    
    ```python
    import pytest
    from unittest.mock import Mock, patch, AsyncMock
    from app.auth import validate_token, TokenError
    
    class TestValidateToken:
        """Tests for validate_token function."""
    
        def test_valid_token_returns_true(self):
            """Should return True for valid JWT token."""
            token = "eyJhbGciOiJIUzI1NiIs..."
            assert validate_token(token) is True
    
        def test_decodes_payload_correctly(self, valid_token):
            """Should decode payload with correct user ID."""
            result = validate_token(valid_token)
            assert result.payload["userId"] == 123
    
        @pytest.mark.parametrize("invalid_input", [
            "",
            "not.a.token",
            "a.b",
            None,
        ])
        def test_rejects_invalid_tokens(self, invalid_input):
            """Should return False for invalid token formats."""
            assert validate_token(invalid_input) is False
    
        def test_rejects_expired_token(self, expired_token):
            """Should return False for expired tokens."""
            assert validate_token(expired_token) is False
    
        def test_raises_token_error_for_null(self):
            """Should raise TokenError with descriptive message."""
            with pytest.raises(TokenError, match="Token cannot be null"):
                validate_token(None)
    
        @pytest.fixture
        def valid_token(self):
            """Create a valid test token."""
            return create_test_token({"userId": 123})
    
        @pytest.fixture
        def expired_token(self):
            """Create an expired test token."""
            return create_test_token({"exp": time.time() - 1000})
    
    
    class TestValidateTokenAsync:
        """Tests for async token validation."""
    
        @pytest.mark.asyncio
        async def test_async_validation(self):
            """Should validate token asynchronously."""
            token = create_test_token({"userId": 456})
            result = await validate_token_async(token)
            assert result.valid is True
    
        @pytest.mark.asyncio
        async def test_handles_network_timeout(self):
            """Should handle network timeout gracefully."""
            with patch("app.auth.fetch_public_key", new_callable=AsyncMock) as mock:
                mock.side_effect = TimeoutError()
    
                with pytest.raises(TokenError, match="Validation timeout"):
                    await validate_token_async("token")
    ```
    
    ---
    
    ## Go (Table-Driven Tests)
    
    ```go
    package auth
    
    import (
    	"testing"
    	"time"
    
    	"github.com/stretchr/testify/assert"
    	"github.com/stretchr/testify/require"
    )
    
    func TestValidateToken(t *testing.T) {
    	t.Parallel()
    
    	tests := []struct {
    		name    string
    		token   string
    		want    bool
    		wantErr error
    	}{
    		{
    			name:  "valid token",
    			token: "eyJhbGciOiJIUzI1NiIs...",
    			want:  true,
    		},
    		{
    			name:  "empty string",
    			token: "",
    			want:  false,
    		},
    		{
    			name:  "malformed token",
    			token: "not.a.token",
    			want:  false,
    		},
    		{
    			name:    "nil token",
    			token:   "",
    			wantErr: ErrTokenNil,
    		},
    	}
    
    	for _, tt := range tests {
    		tt := tt // capture range variable
    		t.Run(tt.name, func(t *testing.T) {
    			t.Parallel()
    
    			got, err := ValidateToken(tt.token)
    
    			if tt.wantErr != nil {
    				require.ErrorIs(t, err, tt.wantErr)
    				return
    			}
    
    			require.NoError(t, err)
    			assert.Equal(t, tt.want, got)
    		})
    	}
    }
    
    func TestValidateToken_Expired(t *testing.T) {
    	token := createTestToken(t, TokenClaims{
    		UserID: 123,
    		Exp:    time.Now().Add(-1 * time.Hour),
    	})
    
    	got, err := ValidateToken(token)
    
    	require.NoError(t, err)
    	assert.False(t, got, "expired token should be invalid")
    }
    
    func TestValidateToken_Integration(t *testing.T) {
    	if testing.Short() {
    		t.Skip("skipping integration test in short mode")
    	}
    
    	client := NewAuthClient(testConfig)
    	token, _ := client.GenerateToken(TestUser)
    
    	got, err := ValidateToken(token)
    
    	require.NoError(t, err)
    	assert.True(t, got)
    }
    
    func BenchmarkValidateToken(b *testing.B) {
    	token := createValidToken()
    
    	b.ResetTimer()
    	for i := 0; i < b.N; i++ {
    		ValidateToken(token)
    	}
    }
    
    func createTestToken(t *testing.T, claims TokenClaims) string {
    	t.Helper()
    	token, err := generateToken(claims, testSecret)
    	require.NoError(t, err)
    	return token
    }
    ```
    
    ---
    
    ## Rust (#[test] Patterns)
    
    ```rust
    use crate::auth::{validate_token, TokenError, TokenClaims};
    use std::time::{Duration, SystemTime};
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn valid_token_returns_ok() {
            let token = create_test_token(TokenClaims {
                user_id: 123,
                exp: future_time(),
            });
    
            let result = validate_token(&token);
    
            assert!(result.is_ok());
            assert!(result.unwrap());
        }
    
        #[test]
        fn empty_token_returns_false() {
            let result = validate_token("");
    
            assert!(result.is_ok());
            assert!(!result.unwrap());
        }
    
        #[test]
        fn malformed_token_returns_false() {
            let result = validate_token("not.a.token");
    
            assert!(result.is_ok());
            assert!(!result.unwrap());
        }
    
        #[test]
        fn expired_token_returns_false() {
            let token = create_test_token(TokenClaims {
                user_id: 123,
                exp: past_time(),
            });
    
            let result = validate_token(&token);
    
            assert!(result.is_ok());
            assert!(!result.unwrap(), "expired token should be invalid");
        }
    
        #[test]
        #[should_panic(expected = "Token cannot be null")]
        fn null_token_panics() {
            validate_token_unchecked(None);
        }
    
        #[test]
        fn returns_token_error_for_invalid_signature() {
            let token = "eyJhbGciOiJIUzI1NiIs.tampered.signature";
    
            let result = validate_token(token);
    
            assert!(matches!(result, Err(TokenError::InvalidSignature)));
        }
    
        #[test]
        fn rejects_various_invalid_tokens() {
            let invalid_tokens = [
                ("empty", ""),
                ("single_part", "abc"),
                ("two_parts", "a.b"),
                ("whitespace", "   "),
                ("special_chars", "!@#$%"),
            ];
    
            for (name, token) in invalid_tokens {
                let result = validate_token(token);
                assert!(
                    result.is_ok() && !result.unwrap(),
                    "case '{}' should return false",
                    name
                );
            }
        }
    
        #[tokio::test]
        async fn async_validation_works() {
            let token = create_test_token(valid_claims());
    
            let result = validate_token_async(&token).await;
    
            assert!(result.is_ok());
        }
    
        fn create_test_token(claims: TokenClaims) -> String {
            crate::auth::generate_token(&claims, TEST_SECRET).unwrap()
        }
    
        fn valid_claims() -> TokenClaims {
            TokenClaims {
                user_id: 123,
                exp: future_time(),
            }
        }
    
        fn future_time() -> SystemTime {
            SystemTime::now() + Duration::from_secs(3600)
        }
    
        fn past_time() -> SystemTime {
            SystemTime::now() - Duration::from_secs(3600)
        }
    
        const TEST_SECRET: &[u8] = b"test_secret_key";
    }
    
    #[cfg(test)]
    mod property_tests {
        use super::*;
        use proptest::prelude::*;
    
        proptest! {
            #[test]
            fn doesnt_crash_on_arbitrary_input(s in "\\PC*") {
                let _ = validate_token(&s);
            }
    
            #[test]
            fn valid_tokens_always_validate(user_id in 1u64..1000000) {
                let token = create_test_token(TokenClaims {
                    user_id,
                    exp: future_time(),
                });
    
                let result = validate_token(&token);
                prop_assert!(result.is_ok());
                prop_assert!(result.unwrap());
            }
        }
    }
    ```
    
    ---
    
    ## PHPUnit (PHP)
    
    ```php
    <?php
    
    namespace Tests\Unit\Services;
    
    use PHPUnit\Framework\TestCase;
    use App\Services\AuthService;
    use App\Exceptions\TokenException;
    use Mockery;
    
    class AuthServiceTest extends TestCase
    {
        private AuthService $service;
    
        protected function setUp(): void
        {
            parent::setUp();
            $this->service = new AuthService();
        }
    
        protected function tearDown(): void
        {
            Mockery::close();
            parent::tearDown();
        }
    
        /** @test */
        public function it_validates_correct_token(): void
        {
            $token = $this->createValidToken(['user_id' => 123]);
    
            $result = $this->service->validateToken($token);
    
            $this->assertTrue($result);
        }
    
        /** @test */
        public function it_rejects_expired_token(): void
        {
            $token = $this->createExpiredToken();
    
            $result = $this->service->validateToken($token);
    
            $this->assertFalse($result);
        }
    
        /** @test */
        public function it_throws_for_null_token(): void
        {
            $this->expectException(TokenException::class);
            $this->expectExceptionMessage('Token cannot be null');
    
            $this->service->validateToken(null);
        }
    
        /**
         * @test
         * @dataProvider invalidTokenProvider
         */
        public function it_rejects_invalid_tokens(string $invalidToken): void
        {
            $result = $this->service->validateToken($invalidToken);
    
            $this->assertFalse($result);
        }
    
        public static function invalidTokenProvider(): array
        {
            return [
                'empty string' => [''],
                'malformed' => ['not.a.token'],
                'missing parts' => ['a.b'],
            ];
        }
    }
    ```
    
    ---
    
    ## Pest (PHP)
    
    ```php
    <?php
    
    use App\Services\AuthService;
    use App\Exceptions\TokenException;
    
    describe('AuthService', function () {
        beforeEach(function () {
            $this->service = new AuthService();
        });
    
        describe('validateToken', function () {
            it('validates correct token', function () {
                $token = createValidToken(['user_id' => 123]);
    
                expect($this->service->validateToken($token))->toBeTrue();
            });
    
            it('rejects expired token', function () {
                $token = createExpiredToken();
    
                expect($this->service->validateToken($token))->toBeFalse();
            });
    
            it('throws for null token', function () {
                $this->service->validateToken(null);
            })->throws(TokenException::class, 'Token cannot be null');
    
            it('rejects invalid tokens', function (string $invalidToken) {
                expect($this->service->validateToken($invalidToken))->toBeFalse();
            })->with([
                'empty string' => '',
                'malformed' => 'not.a.token',
                'missing parts' => 'a.b',
            ]);
        });
    });
    ```
    
    ---
    
    ## Cypress (E2E)
    
    ```typescript
    describe('Login Flow', () => {
      beforeEach(() => {
        cy.visit('/login');
      });
    
      it('should login with valid credentials', () => {
        cy.get('[data-cy=email]').type('user@example.com');
        cy.get('[data-cy=password]').type('password123');
        cy.get('[data-cy=submit]').click();
    
        cy.url().should('include', '/dashboard');
        cy.get('[data-cy=welcome]').should('contain', 'Welcome');
      });
    
      it('should show error with invalid credentials', () => {
        cy.intercept('POST', '/api/login', {
          statusCode: 401,
          body: { error: 'Invalid credentials' },
        }).as('loginRequest');
    
        cy.get('[data-cy=email]').type('user@example.com');
        cy.get('[data-cy=password]').type('wrong');
        cy.get('[data-cy=submit]').click();
    
        cy.wait('@loginRequest');
        cy.get('[data-cy=error]').should('be.visible');
        cy.url().should('include', '/login');
      });
    
      it('should persist session after reload', () => {
        cy.login('user@example.com', 'password123');
        cy.visit('/dashboard');
        cy.reload();
    
        cy.get('[data-cy=welcome]').should('be.visible');
      });
    });
    ```
    
    ---
    
    ## Cypress (Component)
    
    ```typescript
    import LoginForm from './LoginForm.vue';
    
    describe('LoginForm Component', () => {
      it('renders login form', () => {
        cy.mount(LoginForm);
    
        cy.get('[data-cy=email]').should('exist');
        cy.get('[data-cy=password]').should('exist');
        cy.get('[data-cy=submit]').should('contain', 'Login');
      });
    
      it('emits submit event with credentials', () => {
        const onSubmitSpy = cy.spy().as('submitSpy');
        cy.mount(LoginForm, { props: { onSubmit: onSubmitSpy } });
    
        cy.get('[data-cy=email]').type('user@example.com');
        cy.get('[data-cy=password]').type('password123');
        cy.get('[data-cy=submit]').click();
    
        cy.get('@submitSpy').should('have.been.calledWith', {
          email: 'user@example.com',
          password: 'password123',
        });
      });
    
      it('validates email format', () => {
        cy.mount(LoginForm);
    
        cy.get('[data-cy=email]').type('invalid-email');
        cy.get('[data-cy=submit]').click();
    
        cy.get('[data-cy=email-error]').should('contain', 'Invalid email');
      });
    });
    ```
    
    ---
    
    ## Playwright (E2E)
    
    ```typescript
    import { test, expect } from '@playwright/test';
    
    test.describe('Login Flow', () => {
      test.beforeEach(async ({ page }) => {
        await page.goto('/login');
      });
    
      test('should login with valid credentials', async ({ page }) => {
        await page.getByRole('textbox', { name: /email/i }).fill('user@example.com');
        await page.getByRole('textbox', { name: /password/i }).fill('password123');
        await page.getByRole('button', { name: /submit/i }).click();
    
        await expect(page).toHaveURL(/dashboard/);
        await expect(page.getByText(/welcome/i)).toBeVisible();
      });
    
      test('should show error with invalid credentials', async ({ page }) => {
        await page.route('**/api/login', async (route) => {
          await route.fulfill({
            status: 401,
            body: JSON.stringify({ error: 'Invalid credentials' }),
          });
        });
    
        await page.getByRole('textbox', { name: /email/i }).fill('user@example.com');
        await page.getByRole('textbox', { name: /password/i }).fill('wrong');
        await page.getByRole('button', { name: /submit/i }).click();
    
        await expect(page.getByTestId('error-message')).toBeVisible();
        await expect(page).toHaveURL(/login/);
      });
    
      test('should validate email format', async ({ page }) => {
        await page.getByRole('textbox', { name: /email/i }).fill('invalid-email');
        await page.getByRole('button', { name: /submit/i }).click();
    
        await expect(page.getByText(/invalid email/i)).toBeVisible();
      });
    
      test('should persist session after reload', async ({ page, context }) => {
        await page.getByRole('textbox', { name: /email/i }).fill('user@example.com');
        await page.getByRole('textbox', { name: /password/i }).fill('password123');
        await page.getByRole('button', { name: /submit/i }).click();
        await expect(page).toHaveURL(/dashboard/);
    
        await page.reload();
        await expect(page.getByText(/welcome/i)).toBeVisible();
      });
    
      test('should be accessible', async ({ page }) => {
        const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
        expect(accessibilityScanResults.violations).toEqual([]);
      });
    });
    
    test.describe('Login Form - Visual', () => {
      test('matches snapshot', async ({ page }) => {
        await page.goto('/login');
        await expect(page).toHaveScreenshot('login-form.png');
      });
    
      test('error state matches snapshot', async ({ page }) => {
        await page.goto('/login');
        await page.getByRole('textbox', { name: /email/i }).fill('invalid');
        await page.getByRole('button', { name: /submit/i }).click();
    
        await expect(page).toHaveScreenshot('login-form-error.png');
      });
    });
    ```
    
  • SKILL.md 10.2 KB
    ---
    name: testgen
    description: "Generate tests with skill-preloaded routing, framework detection, and auto-TaskCreate. Triggers on: generate tests, write tests, testgen, create test file, add test coverage."
    when_to_use: "Use when the user asks to generate or expand tests for a file, function, or directory — e.g. 'write tests for auth.ts', 'add coverage for the parser', 'create a test file for this module'."
    argument-hint: "<target> [--focus happy|edge|error|all] [--depth quick|normal|thorough] [--visual]"
    license: MIT
    allowed-tools: "Read Write Edit Bash Glob Grep Task TaskCreate"
    metadata:
      author: claude-mods
    ---
    
    # TestGen Skill - AI Test Generation
    
    Generate comprehensive tests with automatic framework detection, skill-preloaded general-purpose dispatch, and project convention matching.
    
    ## Architecture
    
    ```
    testgen <target> [--type] [--focus] [--depth]
        │
        ├─→ Step 1: Analyze Target
        │     ├─ File exists? → Read and parse
        │     ├─ Function specified? → Extract signature
        │     ├─ Directory? → List source files
        │     └─ Find existing tests (avoid duplicates)
        │
        ├─→ Step 2: Detect Framework (parallel)
        │     ├─ package.json → jest/vitest/mocha/cypress/playwright
        │     ├─ pyproject.toml → pytest/unittest
        │     ├─ go.mod → go test
        │     ├─ Cargo.toml → cargo test
        │     ├─ composer.json → phpunit/pest
        │     └─ Check existing test patterns
        │
        ├─→ Step 3: Load Project Standards
        │     ├─ AGENTS.md, CLAUDE.md conventions
        │     ├─ Existing test file structure
        │     └─ Naming conventions (*.test.ts vs *.spec.ts)
        │
        ├─→ Step 4: Route to Test Generator (general-purpose + skill preload)
        │     ├─ .ts → general-purpose, preload typescript-ops
        │     ├─ .tsx/.jsx → general-purpose, preload react-ops
        │     ├─ .vue → general-purpose, preload vue-ops
        │     ├─ .py → general-purpose, preload python-pytest-ops
        │     ├─ .go → general-purpose, preload go-ops
        │     ├─ .rs → general-purpose, preload rust-ops
        │     ├─ .php → general-purpose, preload laravel-ops
        │     ├─ E2E/Cypress → general-purpose, preload cypress-ops
        │     ├─ Playwright → general-purpose, preload typescript-ops
        │     ├─ Shell/bash → general-purpose, preload bash-ops
        │     ├─ --visual → Chrome DevTools MCP
        │     └─ Multi-file → parallel general-purpose dispatch
        │
        ├─→ Step 5: Generate Tests
        │     ├─ Create test file in correct location
        │     ├─ Follow detected conventions
        │     └─ Include: happy path, edge cases, error handling
        │
        └─→ Step 6: Integration
              ├─ Auto-create task (TaskCreate) for verification
              └─ Suggest: run tests, /review, /save
    ```
    
    ## Execution Steps
    
    ### Step 1: Analyze Target
    
    ```bash
    # Check if target exists
    test -f "$TARGET" && echo "FILE" || test -d "$TARGET" && echo "DIRECTORY"
    
    # For function-specific: extract signature
    command -v ast-grep >/dev/null 2>&1 && ast-grep -p "function $FUNCTION_NAME" "$FILE"
    
    # Fallback to ripgrep
    rg "(?:function|const|def|public|private)\s+$FUNCTION_NAME" "$FILE" -A 10
    ```
    
    **Check for existing tests:**
    ```bash
    fd -e test.ts -e spec.ts -e test.js -e spec.js | rg "$BASENAME"
    fd "test_*.py" | rg "$BASENAME"
    ```
    
    ### Step 2: Detect Framework
    
    **JavaScript/TypeScript:**
    ```bash
    cat package.json 2>/dev/null | jq -r '.devDependencies | keys[]' | grep -E 'jest|vitest|mocha|cypress|playwright|@testing-library'
    ```
    
    **Python:**
    ```bash
    grep -E "pytest|unittest|nose" pyproject.toml setup.py requirements*.txt 2>/dev/null
    ```
    
    **Go:**
    ```bash
    test -f go.mod && echo "go test available"
    ```
    
    **Rust:**
    ```bash
    test -f Cargo.toml && echo "cargo test available"
    ```
    
    **PHP:**
    ```bash
    cat composer.json 2>/dev/null | jq -r '.["require-dev"] | keys[]' | grep -E 'phpunit|pest|codeception'
    ```
    
    ### Step 3: Load Project Standards
    
    ```bash
    # Claude Code conventions
    cat AGENTS.md 2>/dev/null | head -50
    cat CLAUDE.md 2>/dev/null | head -50
    
    # Test config files
    cat jest.config.* vitest.config.* pytest.ini pyproject.toml 2>/dev/null | head -30
    ```
    
    **Test location conventions:**
    ```
    # JavaScript
    src/utils/helper.ts → src/utils/__tests__/helper.test.ts  # __tests__ folder
                        → src/utils/helper.test.ts            # co-located
                        → tests/utils/helper.test.ts          # separate tests/
    
    # Python
    app/utils/helper.py → tests/test_helper.py               # tests/ folder
                        → tests/utils/test_helper.py         # mirror structure
    
    # Go
    pkg/auth/token.go → pkg/auth/token_test.go               # co-located (required)
    
    # Rust
    src/auth.rs → src/auth.rs (mod tests { ... })            # inline tests
                → tests/auth_test.rs                          # integration tests
    ```
    
    ### Step 4: Route to Test Generator
    
    Dispatch is skills-first: the generic `general-purpose` subagent preloads the relevant `-ops` skill before generating tests.
    
    | File Pattern | Dispatch | Preload |
    |--------------|----------|---------|
    | `*.ts` | general-purpose | `skills/typescript-ops/SKILL.md` |
    | `*.tsx`, `*.jsx` | general-purpose | `skills/react-ops/SKILL.md` + `skills/typescript-ops/SKILL.md` |
    | `*.vue` | general-purpose | `skills/vue-ops/SKILL.md` + `skills/typescript-ops/SKILL.md` |
    | `*.py` | general-purpose | `skills/python-pytest-ops/SKILL.md` |
    | `*.go` | general-purpose | `skills/go-ops/SKILL.md` |
    | `*.rs` | general-purpose | `skills/rust-ops/SKILL.md` |
    | `*.php` | general-purpose | `skills/laravel-ops/SKILL.md` |
    | `*.cy.ts`, `cypress/*` | general-purpose | `skills/cypress-ops/SKILL.md` + `skills/typescript-ops/SKILL.md` |
    | `*.spec.ts` (Playwright) | general-purpose | `skills/playwright-ops/SKILL.md` + `skills/typescript-ops/SKILL.md` |
    | `playwright/*`, `e2e/*` | general-purpose | `skills/playwright-ops/SKILL.md` + `skills/typescript-ops/SKILL.md` |
    | `*.sh`, `*.bash` | general-purpose | `skills/bash-ops/SKILL.md` |
    | (--visual flag) | Chrome DevTools MCP | `skills/typescript-ops/SKILL.md` |
    
    **Invoke via Task tool:**
    ```
    Task tool with subagent_type: "general-purpose" (or surviving specialist from table)
    model: "sonnet"
    Prompt includes:
      - Skill preloading (domain knowledge):
        "First, read these files for testing context:
         - Read: skills/security-ops/references/owasp-detailed.md
         - Read: skills/testing-ops/SKILL.md
         - Read: [Preload column for the matched file pattern]"
      - Source file content
      - Function signatures to test
      - Detected framework and conventions
      - Requested test type and focus
    ```
    
    **Language-specific preloads** (append to the preloading section above):
    
    | Language | Additional Preload | Why |
    |----------|-------------------|-----|
    | Python | `skills/python-pytest-ops/SKILL.md` | Fixtures, marks, parametrize, async testing |
    | Go | `skills/go-ops/SKILL.md` | Table-driven tests, benchmarks, testify |
    | Rust | `skills/rust-ops/SKILL.md` | Property testing, criterion, proptest |
    
    ### Step 5: Generate Tests
    
    **Test categories based on --focus:**
    
    | Focus | What to Generate |
    |-------|------------------|
    | `happy` | Normal input, expected output |
    | `edge` | Boundary values, empty inputs, nulls |
    | `error` | Invalid inputs, exceptions, error handling |
    | `all` | All of the above (default) |
    
    **Depth levels:**
    
    | Depth | Coverage |
    |-------|----------|
    | `quick` | Happy path only, 1-2 tests per function |
    | `normal` | Happy + common edge cases (default) |
    | `thorough` | Comprehensive: all paths, mocking, async |
    
    ### Step 6: Integration
    
    **Auto-create task:**
    ```
    TaskCreate:
      subject: "Run generated tests for src/auth.ts"
      description: "Verify generated tests pass and review edge cases"
      activeForm: "Running generated tests for auth.ts"
    ```
    
    **Suggest next steps:**
    ```
    Tests generated: src/auth.test.ts
    
    Next steps:
    1. Run tests: npm test src/auth.test.ts
    2. Review and refine edge cases
    3. Use /save to persist tasks across sessions
    ```
    
    ---
    
    ## Routing Details
    
    ### TypeScript/JavaScript (preload typescript-ops)
    - Proper type imports
    - Generic type handling
    - Async/await patterns
    - Mock typing
    
    ### React/JSX (preload react-ops)
    - React Testing Library patterns
    - Component rendering tests
    - Hook testing (renderHook)
    - Accessibility queries (getByRole)
    
    ### Vue (preload vue-ops)
    - Vue Test Utils patterns
    - Composition API testing
    - Pinia store mocking
    
    ### Python (preload python-pytest-ops)
    - pytest fixtures
    - Parametrized tests
    - Mock/patch patterns
    - Async test handling
    
    ### Go (preload go-ops)
    - Table-driven tests (`[]struct` pattern)
    - `testing.T` and subtests (`t.Run`)
    - Testify assertions (when detected)
    - Benchmark functions (`testing.B`)
    - Parallel tests (`t.Parallel()`)
    
    ### Rust (preload rust-ops)
    - `#[test]` attribute functions
    - `#[cfg(test)]` module organization
    - `#[should_panic]` for error testing
    - proptest/quickcheck for property testing
    
    ### PHP/Laravel (preload laravel-ops)
    - PHPUnit/Pest patterns
    - Database transactions
    - Factory usage
    
    ### E2E/Cypress (preload cypress-ops)
    - Page object patterns
    - Custom commands
    - Network stubbing
    
    ### Shell/bash (preload bash-ops)
    - bats / shunit2 patterns
    - Stubbing external commands
    - Exit-code and stderr assertions
    
    ### Playwright (preload typescript-ops)
    - Page object model patterns
    - Locator strategies
    - Visual regression testing
    
    ---
    
    ## CLI Tool Integration
    
    | Tool | Purpose | Fallback |
    |------|---------|----------|
    | `jq` | Parse package.json | Read tool |
    | `rg` | Find existing tests | Grep tool |
    | `ast-grep` | Parse function signatures | ripgrep patterns |
    | `fd` | Find test files | Glob tool |
    | Chrome DevTools MCP | Visual testing (--visual) | Playwright/Cypress |
    
    **Graceful degradation:**
    ```bash
    command -v jq >/dev/null 2>&1 && cat package.json | jq '.devDependencies' || cat package.json
    ```
    
    ---
    
    ## Reference Files
    
    For framework-specific code examples, see:
    - `frameworks.md` - Complete test examples for all supported languages
    - `visual-testing.md` - Chrome DevTools integration for --visual flag
    
    ---
    
    ## Integration
    
    | Command | Relationship |
    |---------|--------------|
    | `/review` | Review generated tests before committing |
    | `/explain` | Understand complex code before testing |
    | `/save` | Track test coverage goals |
    
  • visual-testing.md 4.6 KB
    # Visual Testing with Chrome DevTools
    
    Documentation for the `--visual` flag which uses Chrome DevTools MCP for interactive visual testing.
    
    ---
    
    ## Overview
    
    The `--visual` flag enables browser-based testing using Chrome DevTools MCP tools. This provides real browser verification, accessibility snapshots, and visual regression testing.
    
    ```bash
    # Visual test with Chrome DevTools
    /testgen src/pages/Login.tsx --visual
    
    # Combined with E2E type
    /testgen src/components/Form.tsx --type e2e --visual
    ```
    
    ---
    
    ## How It Works
    
    1. **Detects Chrome DevTools MCP availability** via `mcp__chrome_devtools__*` tools
    
    2. **For each component/page:**
       - Launches browser with `mcp__chrome_devtools__navigate_page`
       - Takes accessibility snapshot with `mcp__chrome_devtools__take_snapshot`
       - Captures screenshot with `mcp__chrome_devtools__take_screenshot`
       - Tests interactions with `mcp__chrome_devtools__click`, `mcp__chrome_devtools__fill`
    
    3. **Generates test documentation with:**
       - Visual regression baselines
       - Accessibility tree verification
       - Interactive element mapping
    
    ---
    
    ## Chrome DevTools MCP Tools
    
    | Tool | Purpose |
    |------|---------|
    | `navigate_page` | Load component in browser |
    | `take_snapshot` | Get accessibility tree for element refs |
    | `take_screenshot` | Capture visual state |
    | `click` | Test interactive elements |
    | `fill` | Test form inputs |
    | `list_console_messages` | Capture JS errors |
    | `list_network_requests` | Verify API calls |
    | `performance_start_trace` | Performance profiling |
    
    ---
    
    ## When to Use --visual
    
    | Scenario | Recommended |
    |----------|-------------|
    | Component styling verification | Yes |
    | Form interaction testing | Yes |
    | Accessibility compliance | Yes |
    | Unit testing pure functions | No |
    | API integration tests | No |
    
    ---
    
    ## Example Output
    
    ```markdown
    ## Visual Test Results: LoginForm
    
    ### Accessibility Snapshot
    - Form: role="form", aria-label="Login"
    - Email input: role="textbox", name="email"
    - Password input: role="textbox", name="password" (masked)
    - Submit button: role="button", name="Log in"
    
    ### Screenshot
    Captured: login-form-baseline.png
    
    ### Interactive Tests
    - [PASS] Email field accepts input
    - [PASS] Password field masks characters
    - [PASS] Submit button triggers form validation
    - [PASS] Error state displays for invalid email
    
    ### Console Errors
    None detected
    
    ### Suggested Playwright Tests
    [Generated code based on visual analysis]
    ```
    
    ---
    
    ## Claude-in-Chrome Integration
    
    When the `mcp__claude-in-chrome__*` tools are available, `--visual` can also use:
    
    | Tool | Purpose |
    |------|---------|
    | `read_page` | Get comprehensive accessibility tree |
    | `find` | Locate elements by natural language |
    | `computer` | Simulate real user interactions |
    | `get_page_text` | Extract text content |
    
    ---
    
    ## Detection Logic
    
    ```bash
    # Check for Chrome DevTools MCP
    if available(mcp__chrome_devtools__take_snapshot); then
        echo "Chrome DevTools available - enabling visual testing"
    fi
    
    # Check for Claude-in-Chrome
    if available(mcp__claude-in-chrome__read_page); then
        echo "Claude-in-Chrome available - enabling enhanced visual testing"
    fi
    ```
    
    ---
    
    ## Fallback Chain
    
    If Chrome DevTools is not available, `--visual` gracefully degrades:
    
    ```
    Chrome DevTools MCP → Claude-in-Chrome → Playwright → Cypress → Manual testing
    ```
    
    1. **Chrome DevTools**: Real-time browser control via MCP
    2. **Claude-in-Chrome**: Enhanced accessibility and natural language queries
    3. **Playwright**: Generate Playwright test code for manual execution
    4. **Cypress**: Generate Cypress test code for manual execution
    5. **Manual**: Output instructions for manual visual testing
    
    ---
    
    ## Advanced Flags
    
    ### --coverage + --visual
    
    Combine coverage analysis with visual testing:
    
    ```bash
    /testgen src/components/ --coverage --visual
    ```
    
    This identifies untested visual states and generates tests for them.
    
    ### --from-review + --visual
    
    Generate visual regression tests for UI issues found by `/review`:
    
    ```bash
    /testgen --from-review --visual
    ```
    
    ---
    
    ## Output Artifacts
    
    When `--visual` is used, the following artifacts may be created:
    
    | Artifact | Location | Purpose |
    |----------|----------|---------|
    | Screenshots | `tests/__screenshots__/` | Visual regression baselines |
    | Accessibility snapshots | `tests/__a11y__/` | A11y tree for comparison |
    | Generated tests | `tests/visual/` | Playwright/Cypress test files |
    
    ---
    
    ## Integration with CI/CD
    
    Visual tests can be run in CI with Chrome:
    
    ```yaml
    # GitHub Actions example
    - name: Visual Tests
      run: |
        npx playwright test --project=chromium
      env:
        CI: true
    ```
    
    For Chrome DevTools MCP in CI, ensure the browser is launched in headless mode with remote debugging enabled.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related