Compare commits
2 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00d7ef1c9 | ||
|
|
382ee67868 |
8 changed files with 283 additions and 8587 deletions
66
.gitignore
vendored
66
.gitignore
vendored
|
|
@ -1,64 +1,44 @@
|
||||||
# Dependencies
|
# ── Dependencies ──
|
||||||
/schneespur/vendor/
|
|
||||||
/node_modules/
|
/node_modules/
|
||||||
|
/vendor/
|
||||||
|
/schneespur/node_modules/
|
||||||
|
/schneespur/vendor/
|
||||||
|
|
||||||
# Environment
|
# ── Local env (keep .env.example tracked) ──
|
||||||
.env
|
.env
|
||||||
.env.backup
|
.env.*
|
||||||
.env.production
|
!.env.example
|
||||||
|
|
||||||
# IDE / Editor
|
# ── Laravel runtime ──
|
||||||
.idea/
|
|
||||||
.vscode/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Laravel
|
|
||||||
/schneespur/storage/logs/*.log
|
/schneespur/storage/logs/*.log
|
||||||
/schneespur/storage/framework/cache/*
|
/schneespur/storage/framework/cache/*
|
||||||
/schneespur/storage/framework/sessions/*
|
/schneespur/storage/framework/sessions/*
|
||||||
/schneespur/storage/framework/views/*
|
/schneespur/storage/framework/views/*
|
||||||
/schneespur/storage/testing/
|
/schneespur/storage/testing/
|
||||||
|
|
||||||
# Build artifacts
|
# ── Build artifacts ──
|
||||||
/schneespur/public/build/
|
/schneespur/public/build/
|
||||||
/schneespur/public/hot
|
/schneespur/public/hot
|
||||||
|
/release/
|
||||||
|
|
||||||
# Testing
|
# ── Internal dev files (not part of distributable app) ──
|
||||||
|
/build.sh
|
||||||
|
/moduldoku.md
|
||||||
|
/package-lock.json
|
||||||
|
|
||||||
|
# ── Test caches ──
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
.phpunit.cache/
|
.phpunit.cache/
|
||||||
|
|
||||||
# Release builds
|
# ── IDE / OS ──
|
||||||
/release/
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# Misc
|
# ── Local tooling ──
|
||||||
*.bak
|
|
||||||
*.orig
|
|
||||||
|
|
||||||
# ── GSD baseline (auto-generated) ──
|
|
||||||
.gsd
|
.gsd
|
||||||
.gsd-id
|
.gsd-id
|
||||||
.mcp.json
|
.mcp.json
|
||||||
.bg-shell/
|
.bg-shell/
|
||||||
*~
|
|
||||||
*.code-workspace
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
node_modules/
|
|
||||||
.next/
|
|
||||||
dist/
|
|
||||||
build/
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
.venv/
|
|
||||||
venv/
|
|
||||||
target/
|
|
||||||
vendor/
|
|
||||||
*.log
|
|
||||||
coverage/
|
|
||||||
.cache/
|
|
||||||
tmp/
|
|
||||||
|
|
|
||||||
215
build.sh
215
build.sh
|
|
@ -1,215 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
VERSION="${1:-1.0.0}"
|
|
||||||
PRODUCT="schneespur"
|
|
||||||
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
SOURCE_DIR="${PROJECT_DIR}/${PRODUCT}"
|
|
||||||
BUILD_DIR="${PROJECT_DIR}/release/${PRODUCT}-${VERSION}"
|
|
||||||
ZIP_FILE="${PROJECT_DIR}/release/${PRODUCT}-${VERSION}.zip"
|
|
||||||
|
|
||||||
echo "════════════════════════════════════════════"
|
|
||||||
echo " Building ${PRODUCT} v${VERSION}"
|
|
||||||
echo "════════════════════════════════════════════"
|
|
||||||
|
|
||||||
cd "$PROJECT_DIR"
|
|
||||||
|
|
||||||
# ── Clean previous build ──
|
|
||||||
rm -rf "$BUILD_DIR" "$ZIP_FILE"
|
|
||||||
mkdir -p "$BUILD_DIR"
|
|
||||||
|
|
||||||
# ── 1. Frontend build ──
|
|
||||||
echo ""
|
|
||||||
echo "▸ Installing npm dependencies..."
|
|
||||||
(cd "$SOURCE_DIR" && (npm ci --silent 2>/dev/null || npm install --silent))
|
|
||||||
|
|
||||||
echo "▸ Building frontend assets..."
|
|
||||||
(cd "$SOURCE_DIR" && npm run build)
|
|
||||||
|
|
||||||
# ── 2. Composer production install ──
|
|
||||||
echo ""
|
|
||||||
echo "▸ Installing composer dependencies (production)..."
|
|
||||||
(cd "$SOURCE_DIR" && composer install --no-dev --optimize-autoloader --no-interaction --quiet)
|
|
||||||
|
|
||||||
# ── 3. Copy files ──
|
|
||||||
echo ""
|
|
||||||
echo "▸ Copying project files..."
|
|
||||||
|
|
||||||
# Core Laravel → build root (flat layout, like 1.0.0)
|
|
||||||
cp "$SOURCE_DIR/artisan" "$BUILD_DIR/"
|
|
||||||
cp "$SOURCE_DIR/composer.json" "$BUILD_DIR/"
|
|
||||||
cp "$SOURCE_DIR/composer.lock" "$BUILD_DIR/"
|
|
||||||
cp "$SOURCE_DIR/.env.example" "$BUILD_DIR/"
|
|
||||||
cp "$SOURCE_DIR/.editorconfig" "$BUILD_DIR/" 2>/dev/null || true
|
|
||||||
cp "$SOURCE_DIR/.htaccess" "$BUILD_DIR/" 2>/dev/null || true
|
|
||||||
|
|
||||||
# Application code → build root
|
|
||||||
cp -r "$SOURCE_DIR/app" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/bootstrap" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/config" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/database" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/lang" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/public" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/resources" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/routes" "$BUILD_DIR/"
|
|
||||||
cp -r "$SOURCE_DIR/vendor" "$BUILD_DIR/"
|
|
||||||
|
|
||||||
# Modules directory — example module is dev-only and excluded from releases
|
|
||||||
if [ -d "$SOURCE_DIR/modules" ]; then
|
|
||||||
cp -r "$SOURCE_DIR/modules" "$BUILD_DIR/"
|
|
||||||
rm -rf "$BUILD_DIR/modules/example"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Documentation and legal → build root (flat, alongside code)
|
|
||||||
cp "$PROJECT_DIR/README.md" "$BUILD_DIR/" 2>/dev/null || true
|
|
||||||
cp "$PROJECT_DIR/LICENSE" "$BUILD_DIR/" 2>/dev/null || true
|
|
||||||
cp "$PROJECT_DIR/INSTALL.de.md" "$BUILD_DIR/" 2>/dev/null || true
|
|
||||||
cp "$PROJECT_DIR/INSTALL.en.md" "$BUILD_DIR/" 2>/dev/null || true
|
|
||||||
|
|
||||||
# ── 4. Prepare storage structure (empty, writable) ──
|
|
||||||
echo "▸ Preparing storage structure..."
|
|
||||||
rm -rf "$BUILD_DIR/storage"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/app/private"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/app/public"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/framework/cache/data"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/framework/sessions"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/framework/testing"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/framework/views"
|
|
||||||
mkdir -p "$BUILD_DIR/storage/logs"
|
|
||||||
|
|
||||||
for dir in "$BUILD_DIR/storage/app/private" \
|
|
||||||
"$BUILD_DIR/storage/app/public" \
|
|
||||||
"$BUILD_DIR/storage/framework/cache/data" \
|
|
||||||
"$BUILD_DIR/storage/framework/sessions" \
|
|
||||||
"$BUILD_DIR/storage/framework/testing" \
|
|
||||||
"$BUILD_DIR/storage/framework/views" \
|
|
||||||
"$BUILD_DIR/storage/logs"; do
|
|
||||||
touch "$dir/.gitkeep"
|
|
||||||
done
|
|
||||||
|
|
||||||
# ── 5. Remove dev/unnecessary files ──
|
|
||||||
echo "▸ Cleaning up dev files..."
|
|
||||||
|
|
||||||
# Remove installed.lock (fresh install!)
|
|
||||||
rm -f "$BUILD_DIR/storage/app/installed.lock"
|
|
||||||
|
|
||||||
# Remove public/storage symlink (installer creates it)
|
|
||||||
rm -f "$BUILD_DIR/public/storage"
|
|
||||||
|
|
||||||
# Remove bootstrap cache (regenerated on first run)
|
|
||||||
rm -f "$BUILD_DIR/bootstrap/cache/"*.php 2>/dev/null || true
|
|
||||||
|
|
||||||
# Remove test files
|
|
||||||
rm -rf "$BUILD_DIR/tests"
|
|
||||||
|
|
||||||
# Remove dev config/tooling
|
|
||||||
rm -f "$BUILD_DIR/phpunit.xml"
|
|
||||||
rm -f "$BUILD_DIR/.styleci.yml"
|
|
||||||
rm -f "$BUILD_DIR/.gitignore"
|
|
||||||
rm -f "$BUILD_DIR/.gitattributes"
|
|
||||||
rm -f "$BUILD_DIR/package.json"
|
|
||||||
rm -f "$BUILD_DIR/package-lock.json"
|
|
||||||
rm -f "$BUILD_DIR/vite.config.js"
|
|
||||||
|
|
||||||
# Remove GSD/AI/editor artifacts
|
|
||||||
rm -rf "$BUILD_DIR/.gsd"
|
|
||||||
rm -f "$BUILD_DIR/.gsd-id"
|
|
||||||
rm -f "$BUILD_DIR/CLAUDE.md"
|
|
||||||
rm -f "$BUILD_DIR/gpt.md"
|
|
||||||
rm -f "$BUILD_DIR/module.md"
|
|
||||||
rm -f "$BUILD_DIR/site.md"
|
|
||||||
rm -f "$BUILD_DIR/.mcp.json"
|
|
||||||
|
|
||||||
# Remove test/scratch files
|
|
||||||
rm -f "$BUILD_DIR/test.txt"
|
|
||||||
rm -f "$BUILD_DIR/test-file.txt"
|
|
||||||
rm -f "$BUILD_DIR/app/test.php"
|
|
||||||
|
|
||||||
# Remove database factories/seeders (not needed in production)
|
|
||||||
rm -rf "$BUILD_DIR/database/factories"
|
|
||||||
rm -rf "$BUILD_DIR/database/seeders"
|
|
||||||
|
|
||||||
# ── 6. Slim vendor directory ──
|
|
||||||
echo "▸ Slimming vendor directory..."
|
|
||||||
|
|
||||||
# Remove .git directories from vendor packages (source installs)
|
|
||||||
find "$BUILD_DIR/vendor" -type d -name ".git" -exec rm -rf {} + 2>/dev/null || true
|
|
||||||
|
|
||||||
# Remove tests, docs, and other non-runtime directories
|
|
||||||
find "$BUILD_DIR/vendor" -type d \( \
|
|
||||||
-name "tests" -o \
|
|
||||||
-name "Tests" -o \
|
|
||||||
-name "test" -o \
|
|
||||||
-name "Test" -o \
|
|
||||||
-name "test_files" -o \
|
|
||||||
-name "docs" -o \
|
|
||||||
-name "doc" -o \
|
|
||||||
-name "examples" -o \
|
|
||||||
-name "example" -o \
|
|
||||||
-name ".github" \
|
|
||||||
\) -exec rm -rf {} + 2>/dev/null || true
|
|
||||||
|
|
||||||
# Remove non-runtime files
|
|
||||||
find "$BUILD_DIR/vendor" -type f \( \
|
|
||||||
-name "*.md" -o \
|
|
||||||
-name "*.markdown" -o \
|
|
||||||
-name "CHANGELOG*" -o \
|
|
||||||
-name "CHANGE_LOG*" -o \
|
|
||||||
-name "CHANGES*" -o \
|
|
||||||
-name "UPGRADING*" -o \
|
|
||||||
-name "UPGRADE*" -o \
|
|
||||||
-name "SECURITY*" -o \
|
|
||||||
-name "CONTRIBUTING*" -o \
|
|
||||||
-name "CODE_OF_CONDUCT*" -o \
|
|
||||||
-name ".editorconfig" -o \
|
|
||||||
-name ".gitignore" -o \
|
|
||||||
-name ".gitattributes" -o \
|
|
||||||
-name ".php-cs-fixer*" -o \
|
|
||||||
-name ".php_cs*" -o \
|
|
||||||
-name "phpunit.xml*" -o \
|
|
||||||
-name "phpstan*" -o \
|
|
||||||
-name ".styleci.yml" -o \
|
|
||||||
-name ".travis.yml" -o \
|
|
||||||
-name "Makefile" -o \
|
|
||||||
-name "Dockerfile" -o \
|
|
||||||
-name "docker-compose*" \
|
|
||||||
\) -delete 2>/dev/null || true
|
|
||||||
|
|
||||||
# ── 7. Write version file ──
|
|
||||||
echo "▸ Writing version file..."
|
|
||||||
cat > "$BUILD_DIR/VERSION" << EOF
|
|
||||||
${VERSION}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# ── 8. Write initial update state (prevents self-update to same version) ──
|
|
||||||
echo "▸ Writing initial update state..."
|
|
||||||
cat > "$BUILD_DIR/storage/app/schneespur_update_state.json" << EOF
|
|
||||||
{"current_version":"${VERSION}","last_counter":1}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# ── 9. Create ZIP ──
|
|
||||||
echo ""
|
|
||||||
echo "▸ Creating ZIP archive..."
|
|
||||||
rm -f "${ZIP_FILE}.filepart"
|
|
||||||
(cd "${BUILD_DIR}" && zip -r -q "${ZIP_FILE}" .)
|
|
||||||
|
|
||||||
# ── 10. Summary ──
|
|
||||||
ZIP_SIZE=$(du -h "$ZIP_FILE" | cut -f1)
|
|
||||||
FILE_COUNT=$(find "$BUILD_DIR" -type f | wc -l)
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "════════════════════════════════════════════"
|
|
||||||
echo " ✓ Build complete!"
|
|
||||||
echo ""
|
|
||||||
echo " Version: ${VERSION}"
|
|
||||||
echo " Files: ${FILE_COUNT}"
|
|
||||||
echo " ZIP: ${ZIP_FILE} (${ZIP_SIZE})"
|
|
||||||
echo "════════════════════════════════════════════"
|
|
||||||
echo ""
|
|
||||||
echo " The ZIP is ready for distribution."
|
|
||||||
echo " Users: unzip → FTP upload → open in browser → installer starts"
|
|
||||||
|
|
||||||
# ── 11. Restore dev dependencies ──
|
|
||||||
echo ""
|
|
||||||
echo "▸ Restoring dev composer dependencies..."
|
|
||||||
(cd "$SOURCE_DIR" && composer install --no-interaction --quiet)
|
|
||||||
1046
moduldoku.md
1046
moduldoku.md
File diff suppressed because it is too large
Load diff
7283
package-lock.json
generated
7283
package-lock.json
generated
File diff suppressed because it is too large
Load diff
27
schneespur/phpunit.xml
Normal file
27
schneespur/phpunit.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
cacheDirectory=".phpunit.cache"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Feature">
|
||||||
|
<directory>tests/Feature</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory>app</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
<php>
|
||||||
|
<env name="APP_ENV" value="testing"/>
|
||||||
|
<env name="APP_KEY" value="base64:dGVzdGluZ2tleXRlc3RpbmdrZXl0ZXN0aW5na2V5cw=="/>
|
||||||
|
<env name="DB_CONNECTION" value="sqlite"/>
|
||||||
|
<env name="DB_DATABASE" value=":memory:"/>
|
||||||
|
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||||
|
<env name="MAIL_MAILER" value="array"/>
|
||||||
|
<env name="CACHE_STORE" value="array"/>
|
||||||
|
</php>
|
||||||
|
</phpunit>
|
||||||
17
schneespur/tests/CreatesApplication.php
Normal file
17
schneespur/tests/CreatesApplication.php
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Console\Kernel;
|
||||||
|
|
||||||
|
trait CreatesApplication
|
||||||
|
{
|
||||||
|
public function createApplication(): \Illuminate\Foundation\Application
|
||||||
|
{
|
||||||
|
$app = require __DIR__.'/../bootstrap/app.php';
|
||||||
|
|
||||||
|
$app->make(Kernel::class)->bootstrap();
|
||||||
|
|
||||||
|
return $app;
|
||||||
|
}
|
||||||
|
}
|
||||||
202
schneespur/tests/Feature/FilterRegistryTest.php
Normal file
202
schneespur/tests/Feature/FilterRegistryTest.php
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Services\Extension\FilterRegistry;
|
||||||
|
use App\Services\Extension\NavigationRegistry;
|
||||||
|
use Illuminate\Foundation\Testing\LazilyRefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class FilterRegistryTest extends TestCase
|
||||||
|
{
|
||||||
|
use LazilyRefreshDatabase;
|
||||||
|
|
||||||
|
private FilterRegistry $registry;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->registry = new FilterRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_apply_returns_original_value_when_no_filters_registered(): void
|
||||||
|
{
|
||||||
|
$result = $this->registry->apply('schneespur.nonexistent.hook', ['a', 'b']);
|
||||||
|
|
||||||
|
$this->assertSame(['a', 'b'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_filters_execute_in_priority_order(): void
|
||||||
|
{
|
||||||
|
$order = [];
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) use (&$order) {
|
||||||
|
$order[] = 'priority-200';
|
||||||
|
return $value;
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) use (&$order) {
|
||||||
|
$order[] = 'priority-50';
|
||||||
|
return $value;
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) use (&$order) {
|
||||||
|
$order[] = 'priority-100';
|
||||||
|
return $value;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
$this->registry->apply('test.hook', 'start');
|
||||||
|
|
||||||
|
$this->assertSame(['priority-50', 'priority-100', 'priority-200'], $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_equal_priority_preserves_registration_order(): void
|
||||||
|
{
|
||||||
|
$order = [];
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) use (&$order) {
|
||||||
|
$order[] = 'first';
|
||||||
|
return $value;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) use (&$order) {
|
||||||
|
$order[] = 'second';
|
||||||
|
return $value;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) use (&$order) {
|
||||||
|
$order[] = 'third';
|
||||||
|
return $value;
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
$this->registry->apply('test.hook', 'start');
|
||||||
|
|
||||||
|
$this->assertSame(['first', 'second', 'third'], $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_apply_passes_context_to_callbacks(): void
|
||||||
|
{
|
||||||
|
$receivedContext = [];
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value, $ctxA, $ctxB) use (&$receivedContext) {
|
||||||
|
$receivedContext = [$ctxA, $ctxB];
|
||||||
|
return $value;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->registry->apply('test.hook', 'value', 'context-a', 'context-b');
|
||||||
|
|
||||||
|
$this->assertSame(['context-a', 'context-b'], $receivedContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_throwing_callback_logs_warning_and_preserves_previous_value(): void
|
||||||
|
{
|
||||||
|
Log::shouldReceive('warning')
|
||||||
|
->once()
|
||||||
|
->withArgs(function ($message, $context) {
|
||||||
|
return $message === 'FilterRegistry: callback failed'
|
||||||
|
&& $context['hook'] === 'test.hook'
|
||||||
|
&& str_contains($context['error'], 'Intentional test exception');
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function (array $value) {
|
||||||
|
$value[] = 'before-error';
|
||||||
|
return $value;
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function ($value) {
|
||||||
|
throw new \RuntimeException('Intentional test exception');
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function (array $value) {
|
||||||
|
$value[] = 'after-error';
|
||||||
|
return $value;
|
||||||
|
}, 30);
|
||||||
|
|
||||||
|
$result = $this->registry->apply('test.hook', []);
|
||||||
|
|
||||||
|
$this->assertSame(['before-error', 'after-error'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_callbacks_can_transform_value_through_chain(): void
|
||||||
|
{
|
||||||
|
$this->registry->register('test.hook', function (int $value) {
|
||||||
|
return $value + 10;
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
$this->registry->register('test.hook', function (int $value) {
|
||||||
|
return $value * 2;
|
||||||
|
}, 20);
|
||||||
|
|
||||||
|
$result = $this->registry->apply('test.hook', 5);
|
||||||
|
|
||||||
|
$this->assertSame(30, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_single_callback_executes(): void
|
||||||
|
{
|
||||||
|
$this->registry->register('test.hook', function (array $items) {
|
||||||
|
$items[] = 'added';
|
||||||
|
return $items;
|
||||||
|
});
|
||||||
|
|
||||||
|
$result = $this->registry->apply('test.hook', ['original']);
|
||||||
|
|
||||||
|
$this->assertSame(['original', 'added'], $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_navigation_items_hook_is_applied(): void
|
||||||
|
{
|
||||||
|
$this->bootExampleModule();
|
||||||
|
|
||||||
|
$nav = $this->app->make(NavigationRegistry::class);
|
||||||
|
$nav->addGroup('modules', 'Modules', 100);
|
||||||
|
|
||||||
|
$items = $nav->getItems();
|
||||||
|
|
||||||
|
$allSlugs = [];
|
||||||
|
foreach ($items as $groupItems) {
|
||||||
|
foreach ($groupItems as $item) {
|
||||||
|
$allSlugs[] = $item['slug'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertContains('example-filter', $allSlugs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_dashboard_kpis_hook_is_applied(): void
|
||||||
|
{
|
||||||
|
$this->bootExampleModule();
|
||||||
|
|
||||||
|
$filterRegistry = $this->app->make(FilterRegistry::class);
|
||||||
|
|
||||||
|
$widgets = $filterRegistry->apply('schneespur.dashboard.kpis', [
|
||||||
|
['key' => 'original-widget', 'label' => 'Original'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$keys = array_column($widgets, 'key');
|
||||||
|
|
||||||
|
$this->assertContains('example-filter-widget', $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function bootExampleModule(): void
|
||||||
|
{
|
||||||
|
$modulePath = base_path('modules/example/src');
|
||||||
|
spl_autoload_register(function (string $class) use ($modulePath) {
|
||||||
|
$prefix = 'Schneespur\\Module\\Example\\';
|
||||||
|
if (! str_starts_with($class, $prefix)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$relative = substr($class, strlen($prefix));
|
||||||
|
$file = $modulePath . '/' . str_replace('\\', '/', $relative) . '.php';
|
||||||
|
if (file_exists($file)) {
|
||||||
|
require_once $file;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
putenv('EXAMPLE_MODULE_ENABLED=true');
|
||||||
|
$_ENV['EXAMPLE_MODULE_ENABLED'] = true;
|
||||||
|
|
||||||
|
$this->app->register(\Schneespur\Module\Example\ExampleServiceProvider::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
schneespur/tests/TestCase.php
Normal file
14
schneespur/tests/TestCase.php
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
|
||||||
|
abstract class TestCase extends BaseTestCase
|
||||||
|
{
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
$this->withoutVite();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue