chore: merge stable release v19.5.1 to main #76
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: End to End Spiderly Test | |
| on: | |
| push: | |
| branches: [main, develop, v*] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| env: | |
| DOTNET_VERSION: "9.0.x" | |
| NODE_VERSION: "20" | |
| TEST_APP_NAME: "TestApp" | |
| APP_FOLDER: "test-app" | |
| jobs: | |
| integration-test: | |
| name: Test CLI Generated Application | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_VERSION }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Setup PostgreSQL | |
| run: | | |
| sudo systemctl start postgresql | |
| sudo -u postgres psql -c "ALTER USER postgres WITH PASSWORD 'postgres';" | |
| echo "PostgreSQL setup complete" | |
| - name: Install EF Core tools | |
| run: dotnet tool install --global dotnet-ef --version 9.0.1 | |
| - name: Build and pack Spiderly.CLI | |
| run: | | |
| dotnet restore | |
| dotnet build -c Release | |
| dotnet pack Spiderly.CLI/Spiderly.CLI.csproj -c Release -o ./nupkg | |
| - name: Install Spiderly.CLI | |
| run: | | |
| dotnet tool uninstall --global Spiderly.CLI 2>/dev/null || true | |
| dotnet tool install --global Spiderly.CLI --add-source ./nupkg --prerelease | |
| - name: Setup folder structure for --dev mode | |
| run: | | |
| ln -s . spiderly | |
| - name: Run Spiderly init | |
| run: | | |
| export PATH="$HOME/.dotnet/tools:$PATH" | |
| echo "Running spiderly init with app name: ${{ env.TEST_APP_NAME }}" | |
| spiderly init --dev --name "${{ env.TEST_APP_NAME }}" --db postgresql | |
| if [ $? -ne 0 ]; then | |
| echo "ERROR: spiderly init failed with exit code $?" | |
| exit 1 | |
| fi | |
| - name: Add test entity | |
| run: | | |
| APP_NAME="${{ env.TEST_APP_NAME }}" | |
| ENTITIES_PATH="${{ env.APP_FOLDER }}/Backend/$APP_NAME.Business/Entities" | |
| ENTITY_FILE="$ENTITIES_PATH/Product.cs" | |
| cat > "$ENTITY_FILE" <<'PRODUCTCS' | |
| using System.ComponentModel.DataAnnotations; | |
| using Spiderly.Shared.Attributes.Entity; | |
| using Spiderly.Shared.Attributes.Entity.UI; | |
| using Spiderly.Shared.BaseEntities; | |
| using Spiderly.Shared.Enums; | |
| namespace ${{ env.TEST_APP_NAME }}.Business.Entities | |
| { | |
| [DoNotAuthorize] | |
| public class Product : BusinessObject<int> | |
| { | |
| [DisplayName] | |
| [Required] | |
| [MaxLength(100)] | |
| public string Name { get; set; } | |
| [UIControlType(nameof(UIControlTypeCodes.TextArea))] | |
| [MaxLength(500)] | |
| public string Description { get; set; } | |
| [Required] | |
| [Range(0.01, 999999.99)] | |
| public decimal Price { get; set; } | |
| [Range(0, 999999)] | |
| public int Stock { get; set; } | |
| public bool? IsActive { get; set; } | |
| } | |
| } | |
| PRODUCTCS | |
| echo "✓ Created test entity: Product.cs" | |
| - name: Build .NET backend | |
| run: | | |
| BACKEND_PATH="${{ env.APP_FOLDER }}/Backend" | |
| echo "Building .NET backend..." | |
| cd "$BACKEND_PATH" | |
| dotnet restore | |
| if [ $? -ne 0 ]; then exit 1; fi | |
| dotnet build -c Release --no-restore | |
| if [ $? -ne 0 ]; then exit 1; fi | |
| echo "✓ Backend build successful" | |
| - name: Run EF migration for Product entity | |
| run: | | |
| INFRASTRUCTURE_PATH="${{ env.APP_FOLDER }}/Backend/${{ env.TEST_APP_NAME }}.Infrastructure" | |
| cd "$INFRASTRUCTURE_PATH" | |
| echo "Adding migration for Product entity..." | |
| dotnet ef migrations add AddProductEntity --startup-project ../${{ env.TEST_APP_NAME }}.WebAPI/${{ env.TEST_APP_NAME }}.WebAPI.csproj | |
| echo "Updating database..." | |
| dotnet ef database update --startup-project ../${{ env.TEST_APP_NAME }}.WebAPI/${{ env.TEST_APP_NAME }}.WebAPI.csproj | |
| - name: Install Spiderly Angular library dependencies | |
| run: | | |
| echo "Installing Spiderly Angular library dependencies..." | |
| cd spiderly/Angular | |
| npm ci | |
| if [ $? -ne 0 ]; then | |
| echo "ERROR: npm ci for Spiderly Angular failed" | |
| exit 1 | |
| fi | |
| - name: Install frontend dependencies | |
| run: | | |
| FRONTEND_PATH="${{ env.APP_FOLDER }}/Frontend" | |
| echo "Installing frontend packages..." | |
| cd "$FRONTEND_PATH" | |
| npm ci | |
| if [ $? -ne 0 ]; then | |
| echo "ERROR: npm ci failed" | |
| exit 1 | |
| fi | |
| - name: Build Angular frontend | |
| run: | | |
| FRONTEND_PATH="${{ env.APP_FOLDER }}/Frontend" | |
| echo "Building Angular frontend..." | |
| cd "$FRONTEND_PATH" | |
| npm run build | |
| if [ $? -ne 0 ]; then | |
| echo "ERROR: Angular build failed" | |
| exit 1 | |
| fi | |
| echo "✓ Frontend build successful" | |
| - name: Install Playwright | |
| run: | | |
| FRONTEND_PATH="${{ env.APP_FOLDER }}/Frontend" | |
| cd "$FRONTEND_PATH" | |
| echo "Installing Playwright..." | |
| npm install -D @playwright/test | |
| npx playwright install chromium --with-deps | |
| - name: Create E2E tests | |
| run: | | |
| FRONTEND_PATH="${{ env.APP_FOLDER }}/Frontend" | |
| E2E_FOLDER="$FRONTEND_PATH/e2e" | |
| mkdir -p "$E2E_FOLDER" | |
| cat > "$FRONTEND_PATH/playwright.config.ts" <<'PLAYWRIGHTCONFIG' | |
| import { defineConfig, devices } from '@playwright/test'; | |
| export default defineConfig({ | |
| testDir: './e2e', | |
| fullyParallel: false, | |
| forbidOnly: !!process.env.CI, | |
| retries: process.env.CI ? 2 : 0, | |
| workers: 1, | |
| reporter: 'html', | |
| use: { | |
| baseURL: 'http://localhost:4200', | |
| trace: 'on-first-retry', | |
| }, | |
| projects: [ | |
| { | |
| name: 'chromium', | |
| use: { ...devices['Desktop Chrome'] }, | |
| }, | |
| ], | |
| webServer: [ | |
| { | |
| command: 'dotnet run --no-build -c Release --urls=http://localhost:5000', | |
| cwd: '../Backend/${{ env.TEST_APP_NAME }}.WebAPI', | |
| url: 'http://localhost:5000', | |
| timeout: 120000, | |
| reuseExistingServer: false, | |
| }, | |
| { | |
| command: 'npm run start', | |
| url: 'http://localhost:4200', | |
| timeout: 120000, | |
| reuseExistingServer: false, | |
| }, | |
| ], | |
| }); | |
| PLAYWRIGHTCONFIG | |
| cat > "$E2E_FOLDER/product.spec.ts" <<'E2ETEST' | |
| import { test, expect } from '@playwright/test'; | |
| test.describe('Product CRUD Operations', () => { | |
| let productId: number; | |
| test('should create a new product via API', async ({ request }) => { | |
| const response = await request.post('http://localhost:5000/api/Product', { | |
| data: { | |
| name: 'E2E Test Product', | |
| description: 'Created by Playwright E2E test', | |
| price: 99.99, | |
| stock: 100, | |
| isActive: true | |
| } | |
| }); | |
| expect(response.ok()).toBeTruthy(); | |
| const product = await response.json(); | |
| expect(product.id).toBeDefined(); | |
| expect(product.name).toBe('E2E Test Product'); | |
| productId = product.id; | |
| }); | |
| test('should retrieve product via API', async ({ request }) => { | |
| const response = await request.get('http://localhost:5000/api/Product'); | |
| expect(response.ok()).toBeTruthy(); | |
| const products = await response.json(); | |
| expect(Array.isArray(products)).toBeTruthy(); | |
| }); | |
| test('should load the application homepage', async ({ page }) => { | |
| await page.goto('/'); | |
| await expect(page).toHaveTitle(/${{ env.TEST_APP_NAME }}/); | |
| }); | |
| test('should navigate to product list page', async ({ page }) => { | |
| await page.goto('/'); | |
| await page.waitForLoadState('networkidle'); | |
| const productLink = page.locator('text=Product').first(); | |
| if (await productLink.isVisible()) { | |
| await productLink.click(); | |
| await page.waitForURL('**/product**'); | |
| } | |
| }); | |
| test('should update product via API', async ({ request }) => { | |
| if (!productId) test.skip(); | |
| const response = await request.put(`http://localhost:5000/api/Product/${productId}`, { | |
| data: { | |
| id: productId, | |
| name: 'Updated E2E Product', | |
| description: 'Updated by E2E test', | |
| price: 149.99, | |
| stock: 50, | |
| isActive: true | |
| } | |
| }); | |
| expect(response.ok()).toBeTruthy(); | |
| }); | |
| test('should delete product via API', async ({ request }) => { | |
| if (!productId) test.skip(); | |
| const response = await request.delete(`http://localhost:5000/api/Product/${productId}`); | |
| expect(response.ok()).toBeTruthy(); | |
| }); | |
| }); | |
| E2ETEST | |
| echo "✓ Created Playwright E2E tests" | |
| - name: Run Playwright E2E tests | |
| run: | | |
| FRONTEND_PATH="${{ env.APP_FOLDER }}/Frontend" | |
| cd "$FRONTEND_PATH" | |
| echo "Running Playwright E2E tests..." | |
| npx playwright test --reporter=list | |
| if [ $? -ne 0 ]; then | |
| echo "✗ E2E tests failed" | |
| exit 1 | |
| fi | |
| echo "✓ All E2E tests passed!" | |
| - name: Upload Playwright report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: playwright-report | |
| path: test-app/Frontend/playwright-report/ | |
| retention-days: 7 | |
| - name: Test Summary | |
| if: success() | |
| run: | | |
| echo "## Integration Test Summary ✓" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Application Generated:** ${{ env.TEST_APP_NAME }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Tests Completed:" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ Spiderly CLI initialization" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ Project structure generation" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ Entity creation (Product)" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ .NET backend build" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ EF Core migrations" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ Source generators execution" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ Angular frontend build" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✓ Playwright E2E tests (API + UI)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Result:** All integration and E2E tests passed! 🎉" >> $GITHUB_STEP_SUMMARY |