Umfangreiche Erweiterung der Skill-Bibliothek: Neue Skills für Humanisierung (Englisch/PT-BR), Design-Validierung, AI-SEO und Coolify-Deployment inkl. Regelwerke, Presets, Pattern-Referenzen, Testfälle und Automatisierungsskripte. Zusätzliche Skills für Revenue-Centric Design, Pier Cloud, OKF, Lebenslauf- und LinkedIn-Optimierung sowie zahlreiche Referenzdateien, Checklisten und YAML/JSON/Markdown-Templates. Einführung einer vollständigen OpenWiki-Dokumentation mit Architektur-, Domain- und Workflow-Beschreibungen, zentralem Index und automatisierten Updates. Modularer Aufbau, restriktive Lizenzen und umfassende Qualitäts- und Evaluationsmechanismen für alle neuen Inhalte.
388 lines
7.3 KiB
Markdown
388 lines
7.3 KiB
Markdown
# Testing Astro Projects
|
|
|
|
Complete guide for testing Astro projects — from unit/component tests to E2E, link checking, type safety, and CI pipelines.
|
|
|
|
---
|
|
|
|
## 1. Component Testing with Vitest
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
npm install -D vitest @vitest/ui
|
|
```
|
|
|
|
### vitest.config.ts
|
|
|
|
```ts
|
|
/// <reference types="vitest" />
|
|
import { getViteConfig } from 'astro/config';
|
|
|
|
export default getViteConfig({
|
|
test: {
|
|
include: ['tests/**/*.{test,spec}.{js,ts}'],
|
|
},
|
|
});
|
|
```
|
|
|
|
### AstroContainer API
|
|
|
|
The `AstroContainer` API renders Astro components in isolation without a full dev server.
|
|
|
|
```ts
|
|
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
|
|
import { expect, test } from 'vitest';
|
|
import Greeting from '../src/components/Greeting.astro';
|
|
|
|
test('renders greeting with name prop', async () => {
|
|
const container = await AstroContainer.create();
|
|
const result = await container.renderToString(Greeting, {
|
|
props: { name: 'World' },
|
|
});
|
|
|
|
expect(result).toContain('Hello, World');
|
|
});
|
|
```
|
|
|
|
### Testing Props
|
|
|
|
```ts
|
|
test('renders default when no name provided', async () => {
|
|
const container = await AstroContainer.create();
|
|
const result = await container.renderToString(Greeting, {
|
|
props: {},
|
|
});
|
|
|
|
expect(result).toContain('Hello, stranger');
|
|
});
|
|
```
|
|
|
|
### Testing Slots
|
|
|
|
```ts
|
|
import Card from '../src/components/Card.astro';
|
|
|
|
test('renders slot content', async () => {
|
|
const container = await AstroContainer.create();
|
|
const result = await container.renderToString(Card, {
|
|
slots: { default: '<p>Slot content here</p>' },
|
|
});
|
|
|
|
expect(result).toContain('Slot content here');
|
|
});
|
|
```
|
|
|
|
### Testing Conditional Rendering
|
|
|
|
```ts
|
|
import Alert from '../src/components/Alert.astro';
|
|
|
|
test('renders error variant', async () => {
|
|
const container = await AstroContainer.create();
|
|
const result = await container.renderToString(Alert, {
|
|
props: { type: 'error', message: 'Something failed' },
|
|
});
|
|
|
|
expect(result).toContain('class="alert-error"');
|
|
expect(result).toContain('Something failed');
|
|
});
|
|
|
|
test('does not render when hidden', async () => {
|
|
const container = await AstroContainer.create();
|
|
const result = await container.renderToString(Alert, {
|
|
props: { type: 'info', message: 'Hidden', visible: false },
|
|
});
|
|
|
|
expect(result).not.toContain('Hidden');
|
|
});
|
|
```
|
|
|
|
### Run Tests
|
|
|
|
```bash
|
|
npx vitest
|
|
npx vitest --ui # browser UI
|
|
```
|
|
|
|
---
|
|
|
|
## 2. E2E Testing with Playwright
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
npm install -D @playwright/test
|
|
npx playwright install
|
|
```
|
|
|
|
### playwright.config.ts
|
|
|
|
```ts
|
|
import { defineConfig } from '@playwright/test';
|
|
|
|
export default defineConfig({
|
|
testDir: './e2e',
|
|
webServer: {
|
|
command: 'npm run preview',
|
|
port: 4321,
|
|
reuseExistingServer: !process.env.CI,
|
|
},
|
|
use: {
|
|
baseURL: 'http://localhost:4321',
|
|
},
|
|
});
|
|
```
|
|
|
|
> **Note:** Run `astro build` before E2E tests so `preview` has something to serve.
|
|
|
|
### Example: Page Load
|
|
|
|
```ts
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test('homepage loads correctly', async ({ page }) => {
|
|
await page.goto('/');
|
|
await expect(page).toHaveTitle(/My Site/);
|
|
await expect(page.locator('h1')).toBeVisible();
|
|
});
|
|
```
|
|
|
|
### Example: Navigation
|
|
|
|
```ts
|
|
test('navigates to about page', async ({ page }) => {
|
|
await page.goto('/');
|
|
await page.click('a[href="/about"]');
|
|
await expect(page).toHaveURL('/about');
|
|
await expect(page.locator('h1')).toContainText('About');
|
|
});
|
|
```
|
|
|
|
### Example: Dynamic Routes
|
|
|
|
```ts
|
|
test('blog post renders from content collection', async ({ page }) => {
|
|
await page.goto('/blog/first-post');
|
|
await expect(page.locator('article h1')).toBeVisible();
|
|
await expect(page.locator('article')).not.toBeEmpty();
|
|
});
|
|
```
|
|
|
|
### Testing View Transitions
|
|
|
|
```ts
|
|
test('view transitions work between pages', async ({ page }) => {
|
|
await page.goto('/');
|
|
const transitionPromise = page.waitForEvent('load');
|
|
await page.click('a[href="/about"]');
|
|
await transitionPromise;
|
|
await expect(page).toHaveURL('/about');
|
|
});
|
|
```
|
|
|
|
### Run E2E Tests
|
|
|
|
```bash
|
|
npx astro build
|
|
npx playwright test
|
|
npx playwright test --ui # interactive mode
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Link Checking
|
|
|
|
### linkinator
|
|
|
|
Checks all links in the built output for broken references.
|
|
|
|
```bash
|
|
npx astro build
|
|
npx linkinator dist --recurse
|
|
```
|
|
|
|
Options:
|
|
|
|
```bash
|
|
npx linkinator dist --recurse --skip "^https://external-site.com"
|
|
```
|
|
|
|
### CI Integration (GitHub Actions)
|
|
|
|
```yaml
|
|
- name: Check links
|
|
run: npx linkinator dist --recurse --retry --retry-errors
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Type Checking
|
|
|
|
### Astro Template Validation
|
|
|
|
```bash
|
|
npx astro check
|
|
```
|
|
|
|
Validates `.astro` files for type errors in expressions, prop types, and component usage.
|
|
|
|
### TypeScript Checking
|
|
|
|
```bash
|
|
npx tsc --noEmit
|
|
```
|
|
|
|
Validates all `.ts` and `.tsx` files without emitting output.
|
|
|
|
### package.json Scripts
|
|
|
|
```json
|
|
{
|
|
"scripts": {
|
|
"check": "astro check && tsc --noEmit"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Content Collection Validation
|
|
|
|
### Schema Enforcement
|
|
|
|
Content collections validate against Zod schemas at build time. Invalid content **fails the build automatically**:
|
|
|
|
```ts
|
|
// src/content.config.ts
|
|
import { defineCollection, z } from 'astro:content';
|
|
|
|
const blog = defineCollection({
|
|
type: 'content',
|
|
schema: z.object({
|
|
title: z.string(),
|
|
date: z.date(),
|
|
draft: z.boolean().default(false),
|
|
}),
|
|
});
|
|
|
|
export const collections = { blog };
|
|
```
|
|
|
|
A frontmatter error produces:
|
|
|
|
```
|
|
[ERROR] blog → "bad-post.md" frontmatter does not match schema.
|
|
"title" is required.
|
|
```
|
|
|
|
### Draft Filtering
|
|
|
|
Filter drafts in production queries:
|
|
|
|
```astro
|
|
---
|
|
import { getCollection } from 'astro:content';
|
|
|
|
const posts = await getCollection('blog', ({ data }) => {
|
|
return import.meta.env.PROD ? !data.draft : true;
|
|
});
|
|
---
|
|
```
|
|
|
|
Test that drafts are excluded by checking the built output does not contain draft post URLs.
|
|
|
|
---
|
|
|
|
## 6. Pre-Deploy Verification Script
|
|
|
|
Save as `scripts/verify.sh`:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
echo "→ Type checking..."
|
|
npx astro check
|
|
|
|
echo "→ Building..."
|
|
npx astro build
|
|
|
|
echo "→ Checking links..."
|
|
npx linkinator dist --recurse
|
|
|
|
echo "→ Running E2E tests..."
|
|
npx playwright test
|
|
|
|
echo "✓ All checks passed"
|
|
```
|
|
|
|
```bash
|
|
chmod +x scripts/verify.sh
|
|
./scripts/verify.sh
|
|
```
|
|
|
|
---
|
|
|
|
## 7. CI Pipeline (GitHub Actions)
|
|
|
|
Save as `.github/workflows/test.yml`:
|
|
|
|
```yaml
|
|
name: Test
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 20
|
|
cache: npm
|
|
|
|
- run: npm ci
|
|
|
|
- name: Type check
|
|
run: npx astro check && npx tsc --noEmit
|
|
|
|
- name: Build
|
|
run: npx astro build
|
|
|
|
- name: Component tests
|
|
run: npx vitest run
|
|
|
|
- name: Install Playwright
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: E2E tests
|
|
run: npx playwright test
|
|
|
|
- name: Link check
|
|
run: npx linkinator dist --recurse --retry
|
|
|
|
- uses: actions/upload-artifact@v4
|
|
if: failure()
|
|
with:
|
|
name: playwright-report
|
|
path: playwright-report/
|
|
```
|
|
|
|
---
|
|
|
|
## Quick Reference
|
|
|
|
| Task | Command |
|
|
|------|---------|
|
|
| Component tests | `npx vitest` |
|
|
| E2E tests | `npx playwright test` |
|
|
| Type check | `npx astro check && tsc --noEmit` |
|
|
| Link check | `npx linkinator dist --recurse` |
|
|
| Full verification | `./scripts/verify.sh` |
|