Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:43:33 +08:00
commit 624998dbc3
27 changed files with 6036 additions and 0 deletions

224
scripts/generate-test.sh Executable file
View File

@@ -0,0 +1,224 @@
#!/usr/bin/env bash
#
# Generate TYPO3 test class
#
# Usage: ./generate-test.sh <type> <ClassName>
# Example: ./generate-test.sh unit EmailValidator
#
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Parse arguments
TEST_TYPE="$1"
CLASS_NAME="$2"
if [ -z "${TEST_TYPE}" ] || [ -z "${CLASS_NAME}" ]; then
echo "Usage: $0 <type> <ClassName>"
echo
echo "Types:"
echo " unit - Unit test (fast, no database)"
echo " functional - Functional test (with database)"
echo " acceptance - Acceptance test (browser-based)"
echo
echo "Example:"
echo " $0 unit EmailValidator"
echo " $0 functional ProductRepository"
echo " $0 acceptance LoginCest"
exit 1
fi
# Validate test type
case ${TEST_TYPE} in
unit|functional|acceptance)
;;
*)
echo -e "${RED}Error: Invalid test type '${TEST_TYPE}'${NC}"
echo "Valid types: unit, functional, acceptance"
exit 1
;;
esac
# Determine paths
PROJECT_DIR="$(pwd)"
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# Check if Tests directory exists
if [ ! -d "${PROJECT_DIR}/Tests" ]; then
echo -e "${RED}Error: Tests directory not found${NC}"
echo "Run setup-testing.sh first"
exit 1
fi
# Set target directory based on test type
case ${TEST_TYPE} in
unit)
TEST_DIR="${PROJECT_DIR}/Tests/Unit"
TEST_SUFFIX="Test"
;;
functional)
TEST_DIR="${PROJECT_DIR}/Tests/Functional"
TEST_SUFFIX="Test"
;;
acceptance)
TEST_DIR="${PROJECT_DIR}/Tests/Acceptance"
TEST_SUFFIX="Cest"
;;
esac
# Extract namespace from composer.json
NAMESPACE=$(php -r '
$composer = json_decode(file_get_contents("composer.json"), true);
foreach ($composer["autoload"]["psr-4"] ?? [] as $ns => $path) {
if (strpos($path, "Classes") !== false) {
echo rtrim($ns, "\\");
break;
}
}
')
if [ -z "${NAMESPACE}" ]; then
echo -e "${RED}Error: Could not determine namespace from composer.json${NC}"
exit 1
fi
# Determine test file path
TEST_FILE="${TEST_DIR}/${CLASS_NAME}${TEST_SUFFIX}.php"
# Check if file already exists
if [ -f "${TEST_FILE}" ]; then
echo -e "${RED}Error: Test file already exists: ${TEST_FILE}${NC}"
exit 1
fi
# Create test file directory if needed
mkdir -p "$(dirname "${TEST_FILE}")"
echo -e "${GREEN}Generating ${TEST_TYPE} test for ${CLASS_NAME}...${NC}"
# Generate test class based on type
case ${TEST_TYPE} in
unit)
cat > "${TEST_FILE}" << EOF
<?php
declare(strict_types=1);
namespace ${NAMESPACE}\\Tests\\Unit;
use TYPO3\\TestingFramework\\Core\\Unit\\UnitTestCase;
use ${NAMESPACE}\\${CLASS_NAME};
/**
* Unit test for ${CLASS_NAME}
*/
final class ${CLASS_NAME}${TEST_SUFFIX} extends UnitTestCase
{
protected ${CLASS_NAME} \$subject;
protected function setUp(): void
{
parent::setUp();
\$this->subject = new ${CLASS_NAME}();
}
/**
* @test
*/
public function canBeInstantiated(): void
{
self::assertInstanceOf(${CLASS_NAME}::class, \$this->subject);
}
}
EOF
;;
functional)
cat > "${TEST_FILE}" << EOF
<?php
declare(strict_types=1);
namespace ${NAMESPACE}\\Tests\\Functional;
use TYPO3\\TestingFramework\\Core\\Functional\\FunctionalTestCase;
use ${NAMESPACE}\\${CLASS_NAME};
/**
* Functional test for ${CLASS_NAME}
*/
final class ${CLASS_NAME}${TEST_SUFFIX} extends FunctionalTestCase
{
protected ${CLASS_NAME} \$subject;
protected array \$testExtensionsToLoad = [
'typo3conf/ext/your_extension',
];
protected function setUp(): void
{
parent::setUp();
\$this->subject = \$this->get(${CLASS_NAME}::class);
}
/**
* @test
*/
public function canBeInstantiated(): void
{
self::assertInstanceOf(${CLASS_NAME}::class, \$this->subject);
}
}
EOF
# Create fixture file
FIXTURE_FILE="${PROJECT_DIR}/Tests/Functional/Fixtures/${CLASS_NAME}.csv"
if [ ! -f "${FIXTURE_FILE}" ]; then
echo "# Fixture for ${CLASS_NAME}${TEST_SUFFIX}" > "${FIXTURE_FILE}"
echo -e "${GREEN}✓ Created fixture: ${FIXTURE_FILE}${NC}"
fi
;;
acceptance)
cat > "${TEST_FILE}" << EOF
<?php
declare(strict_types=1);
namespace ${NAMESPACE}\\Tests\\Acceptance;
use ${NAMESPACE}\\Tests\\Acceptance\\AcceptanceTester;
/**
* Acceptance test for ${CLASS_NAME/Cest/} workflow
*/
final class ${CLASS_NAME}${TEST_SUFFIX}
{
public function _before(AcceptanceTester \$I): void
{
// Setup before each test
}
public function exampleTest(AcceptanceTester \$I): void
{
\$I->amOnPage('/');
\$I->see('Welcome');
}
}
EOF
;;
esac
echo -e "${GREEN}✓ Created: ${TEST_FILE}${NC}"
echo
echo "Run test:"
echo " vendor/bin/phpunit ${TEST_FILE}"
echo
echo "Or via composer:"
echo " composer ci:test:php:${TEST_TYPE}"

180
scripts/setup-testing.sh Executable file
View File

@@ -0,0 +1,180 @@
#!/usr/bin/env bash
#
# Setup TYPO3 testing infrastructure
#
# This script initializes testing infrastructure for TYPO3 extensions:
# - Composer dependencies
# - PHPUnit configurations
# - Directory structure
# - Optional: Docker Compose for acceptance tests
#
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Script configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
PROJECT_DIR="$(pwd)"
WITH_ACCEPTANCE=false
# Parse arguments
while getopts ":a" opt; do
case ${opt} in
a)
WITH_ACCEPTANCE=true
;;
\?)
echo "Usage: $0 [-a]"
echo " -a Include acceptance testing setup (Docker Compose, Codeception)"
exit 1
;;
esac
done
echo -e "${GREEN}=== TYPO3 Testing Infrastructure Setup ===${NC}"
echo
# Check if composer.json exists
if [ ! -f "${PROJECT_DIR}/composer.json" ]; then
echo -e "${RED}Error: composer.json not found in current directory${NC}"
echo "Please run this script from your TYPO3 extension root directory"
exit 1
fi
# 1. Install testing framework dependencies
echo -e "${YELLOW}[1/6] Installing testing framework dependencies...${NC}"
if ! grep -q "typo3/testing-framework" "${PROJECT_DIR}/composer.json"; then
composer require --dev "typo3/testing-framework:^8.0 || ^9.0" --no-update
echo -e "${GREEN}✓ Added typo3/testing-framework${NC}"
else
echo -e "${GREEN}✓ typo3/testing-framework already present${NC}"
fi
# Install PHPUnit if not present
if ! grep -q "phpunit/phpunit" "${PROJECT_DIR}/composer.json"; then
composer require --dev "phpunit/phpunit:^10.5 || ^11.0" --no-update
echo -e "${GREEN}✓ Added phpunit/phpunit${NC}"
fi
composer update --no-progress
# 2. Create directory structure
echo -e "${YELLOW}[2/6] Creating directory structure...${NC}"
mkdir -p "${PROJECT_DIR}/Tests/Unit"
mkdir -p "${PROJECT_DIR}/Tests/Functional/Fixtures"
mkdir -p "${PROJECT_DIR}/Build/phpunit"
mkdir -p "${PROJECT_DIR}/Build/Scripts"
echo -e "${GREEN}✓ Directories created${NC}"
# 3. Copy PHPUnit configurations
echo -e "${YELLOW}[3/6] Installing PHPUnit configurations...${NC}"
if [ ! -f "${PROJECT_DIR}/Build/phpunit/UnitTests.xml" ]; then
cp "${SKILL_DIR}/templates/UnitTests.xml" "${PROJECT_DIR}/Build/phpunit/"
echo -e "${GREEN}✓ Created UnitTests.xml${NC}"
else
echo -e "${YELLOW}⚠ UnitTests.xml already exists (skipped)${NC}"
fi
if [ ! -f "${PROJECT_DIR}/Build/phpunit/FunctionalTests.xml" ]; then
cp "${SKILL_DIR}/templates/FunctionalTests.xml" "${PROJECT_DIR}/Build/phpunit/"
echo -e "${GREEN}✓ Created FunctionalTests.xml${NC}"
else
echo -e "${YELLOW}⚠ FunctionalTests.xml already exists (skipped)${NC}"
fi
if [ ! -f "${PROJECT_DIR}/Build/phpunit/FunctionalTestsBootstrap.php" ]; then
cp "${SKILL_DIR}/templates/FunctionalTestsBootstrap.php" "${PROJECT_DIR}/Build/phpunit/"
echo -e "${GREEN}✓ Created FunctionalTestsBootstrap.php${NC}"
else
echo -e "${YELLOW}⚠ FunctionalTestsBootstrap.php already exists (skipped)${NC}"
fi
# 4. Create AGENTS.md templates
echo -e "${YELLOW}[4/6] Creating AGENTS.md templates...${NC}"
for dir in "${PROJECT_DIR}/Tests/Unit" "${PROJECT_DIR}/Tests/Functional"; do
if [ ! -f "${dir}/AGENTS.md" ]; then
cp "${SKILL_DIR}/templates/AGENTS.md" "${dir}/"
echo -e "${GREEN}✓ Created ${dir}/AGENTS.md${NC}"
else
echo -e "${YELLOW}${dir}/AGENTS.md already exists (skipped)${NC}"
fi
done
# 5. Setup composer scripts
echo -e "${YELLOW}[5/6] Adding composer test scripts...${NC}"
if ! grep -q "ci:test:php:unit" "${PROJECT_DIR}/composer.json"; then
echo -e "${GREEN} Add these scripts to your composer.json:${NC}"
cat << 'EOF'
"scripts": {
"ci:test": [
"@ci:test:php:lint",
"@ci:test:php:phpstan",
"@ci:test:php:unit",
"@ci:test:php:functional"
],
"ci:test:php:lint": "phplint",
"ci:test:php:phpstan": "phpstan analyze --configuration Build/phpstan.neon --no-progress",
"ci:test:php:unit": "phpunit -c Build/phpunit/UnitTests.xml",
"ci:test:php:functional": "phpunit -c Build/phpunit/FunctionalTests.xml"
}
EOF
else
echo -e "${GREEN}✓ Test scripts already configured${NC}"
fi
# 6. Setup acceptance testing if requested
if [ "${WITH_ACCEPTANCE}" = true ]; then
echo -e "${YELLOW}[6/6] Setting up acceptance testing...${NC}"
# Install Codeception
if ! grep -q "codeception/codeception" "${PROJECT_DIR}/composer.json"; then
composer require --dev codeception/codeception codeception/module-webdriver --no-update
composer update --no-progress
echo -e "${GREEN}✓ Installed Codeception${NC}"
fi
# Create acceptance test directory
mkdir -p "${PROJECT_DIR}/Tests/Acceptance"
# Copy Docker Compose and Codeception config
if [ ! -f "${PROJECT_DIR}/Build/docker-compose.yml" ]; then
cp "${SKILL_DIR}/templates/docker/docker-compose.yml" "${PROJECT_DIR}/Build/"
echo -e "${GREEN}✓ Created docker-compose.yml${NC}"
fi
if [ ! -f "${PROJECT_DIR}/codeception.yml" ]; then
cp "${SKILL_DIR}/templates/docker/codeception.yml" "${PROJECT_DIR}/"
echo -e "${GREEN}✓ Created codeception.yml${NC}"
fi
# Initialize Codeception
if [ ! -d "${PROJECT_DIR}/Tests/Acceptance/_support" ]; then
vendor/bin/codecept bootstrap
echo -e "${GREEN}✓ Initialized Codeception${NC}"
fi
else
echo -e "${YELLOW}[6/6] Skipping acceptance testing setup (use -a flag to include)${NC}"
fi
echo
echo -e "${GREEN}=== Setup Complete ===${NC}"
echo
echo "Next steps:"
echo "1. Generate your first test:"
echo " ${SKILL_DIR}/scripts/generate-test.sh unit MyClass"
echo
echo "2. Run tests:"
echo " composer ci:test:php:unit"
echo " composer ci:test:php:functional"
echo
echo "3. Add CI/CD workflow (optional):"
echo " cp ${SKILL_DIR}/templates/github-actions-tests.yml .github/workflows/tests.yml"

135
scripts/validate-setup.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/usr/bin/env bash
#
# Validate TYPO3 testing infrastructure setup
#
# Checks:
# - Required dependencies
# - PHPUnit configurations
# - Directory structure
# - Docker (for acceptance tests)
#
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
PROJECT_DIR="$(pwd)"
ERRORS=0
WARNINGS=0
echo -e "${GREEN}=== TYPO3 Testing Setup Validation ===${NC}"
echo
# Check composer.json
echo -e "${YELLOW}[1/5] Checking composer.json dependencies...${NC}"
if [ ! -f "${PROJECT_DIR}/composer.json" ]; then
echo -e "${RED}✗ composer.json not found${NC}"
((ERRORS++))
else
if grep -q "typo3/testing-framework" "${PROJECT_DIR}/composer.json"; then
echo -e "${GREEN}✓ typo3/testing-framework installed${NC}"
else
echo -e "${RED}✗ typo3/testing-framework missing${NC}"
((ERRORS++))
fi
if grep -q "phpunit/phpunit" "${PROJECT_DIR}/composer.json"; then
echo -e "${GREEN}✓ phpunit/phpunit installed${NC}"
else
echo -e "${RED}✗ phpunit/phpunit missing${NC}"
((ERRORS++))
fi
fi
# Check PHPUnit configurations
echo -e "${YELLOW}[2/5] Checking PHPUnit configurations...${NC}"
if [ -f "${PROJECT_DIR}/Build/phpunit/UnitTests.xml" ]; then
echo -e "${GREEN}✓ UnitTests.xml present${NC}"
else
echo -e "${RED}✗ UnitTests.xml missing${NC}"
((ERRORS++))
fi
if [ -f "${PROJECT_DIR}/Build/phpunit/FunctionalTests.xml" ]; then
echo -e "${GREEN}✓ FunctionalTests.xml present${NC}"
else
echo -e "${RED}✗ FunctionalTests.xml missing${NC}"
((ERRORS++))
fi
if [ -f "${PROJECT_DIR}/Build/phpunit/FunctionalTestsBootstrap.php" ]; then
echo -e "${GREEN}✓ FunctionalTestsBootstrap.php present${NC}"
else
echo -e "${RED}✗ FunctionalTestsBootstrap.php missing${NC}"
((ERRORS++))
fi
# Check directory structure
echo -e "${YELLOW}[3/5] Checking directory structure...${NC}"
for dir in "Tests/Unit" "Tests/Functional" "Tests/Functional/Fixtures"; do
if [ -d "${PROJECT_DIR}/${dir}" ]; then
echo -e "${GREEN}${dir}/ exists${NC}"
else
echo -e "${YELLOW}${dir}/ missing${NC}"
((WARNINGS++))
fi
done
# Check AGENTS.md files
echo -e "${YELLOW}[4/5] Checking AGENTS.md documentation...${NC}"
for dir in "Tests/Unit" "Tests/Functional"; do
if [ -f "${PROJECT_DIR}/${dir}/AGENTS.md" ]; then
echo -e "${GREEN}${dir}/AGENTS.md present${NC}"
else
echo -e "${YELLOW}${dir}/AGENTS.md missing${NC}"
((WARNINGS++))
fi
done
# Check Docker (optional, for acceptance tests)
echo -e "${YELLOW}[5/5] Checking Docker availability (for acceptance tests)...${NC}"
if command -v docker &> /dev/null; then
echo -e "${GREEN}✓ Docker installed${NC}"
if docker ps &> /dev/null; then
echo -e "${GREEN}✓ Docker daemon running${NC}"
else
echo -e "${YELLOW}⚠ Docker daemon not running${NC}"
((WARNINGS++))
fi
else
echo -e "${YELLOW}⚠ Docker not installed (required for acceptance tests)${NC}"
((WARNINGS++))
fi
# Summary
echo
echo -e "${GREEN}=== Validation Summary ===${NC}"
if [ ${ERRORS} -eq 0 ] && [ ${WARNINGS} -eq 0 ]; then
echo -e "${GREEN}✓ All checks passed!${NC}"
echo
echo "Your testing infrastructure is ready to use."
echo "Generate your first test:"
echo " ~/.claude/skills/typo3-testing/scripts/generate-test.sh unit MyClass"
exit 0
elif [ ${ERRORS} -eq 0 ]; then
echo -e "${YELLOW}${WARNINGS} warnings found${NC}"
echo
echo "Basic setup is complete, but some optional components are missing."
exit 0
else
echo -e "${RED}${ERRORS} errors found${NC}"
if [ ${WARNINGS} -gt 0 ]; then
echo -e "${YELLOW}${WARNINGS} warnings found${NC}"
fi
echo
echo "Run setup script to fix errors:"
echo " ~/.claude/skills/typo3-testing/scripts/setup-testing.sh"
exit 1
fi