1
0
Эх сурвалжийг харах

chore: Dashboard install test (#3639)

Michael Bromley 6 сар өмнө
parent
commit
1d84ac209f

+ 128 - 0
.github/workflows/build_test_dashboard.yml

@@ -0,0 +1,128 @@
+name: 'Build & Test Dashboard'
+
+on:
+  push:
+    branches:
+      - minor
+      - major
+      - master
+    paths:
+      - 'packages/dashboard/**'
+      - 'package.json'
+      - 'package-lock.json'
+  pull_request:
+    branches:
+      - master
+      - major
+      - minor
+    paths:
+      - 'packages/dashboard/**'
+      - 'package.json'
+      - 'package-lock.json'
+  workflow_dispatch:
+
+permissions:
+  contents: read
+  packages: write
+
+env:
+  NODE_OPTIONS: '--max_old_space_size=4096'
+
+defaults:
+  run:
+    shell: bash
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  publish_install:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ ubuntu-latest ]
+        node-version: [ 22.x ]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v4
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v4
+        with:
+          node-version: ${{ matrix.node-version }}
+      - name: Install Verdaccio
+        run: |
+          npm install -g verdaccio
+          npm install -g wait-on
+          tmp_registry_log=`mktemp`
+          mkdir -p $HOME/.config/verdaccio
+          cp -v ./.github/workflows/verdaccio/config.yaml $HOME/.config/verdaccio/config.yaml
+          nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
+          wait-on http://localhost:4873
+          TOKEN_RES=$(curl -XPUT \
+            -H "Content-type: application/json" \
+            -d '{ "name": "test", "password": "test" }' \
+            'http://localhost:4873/-/user/org.couchdb.user:test')
+          TOKEN=$(echo "$TOKEN_RES" | jq -r '.token')
+          npm set //localhost:4873/:_authToken $TOKEN
+      - name: npm install
+        run: |
+          npm install
+        env:
+          CI: true
+      - name: Build & publish dashboard
+        run: |
+          nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
+          wait-on http://localhost:4873
+          npx lerna run build --scope @vendure/common --scope @vendure/core
+          cd packages/dashboard
+          npm run build:standalone
+          npm run build
+          npm version prepatch --preid ci --no-git-tag-version --no-commit-hooks
+          npm publish --no-push --yes --tag ci --registry http://localhost:4873
+      - name: Set up new Vendure app
+        run: |
+          mkdir -p $HOME/install
+          cd $HOME/install
+          nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
+          wait-on http://localhost:4873
+          npx @vendure/create@latest test-app --ci --use-npm --log-level info
+      - name: Install dashboard
+        run: |
+          nohup verdaccio --config $HOME/.config/verdaccio/config.yaml &
+          wait-on http://localhost:4873
+          cd $HOME/install/test-app
+          npm install @vendure/dashboard@ci --registry=http://localhost:4873
+          cp -v $GITHUB_WORKSPACE/.github/workflows/scripts/vite.config.mts ./vite.config.mts
+      - name: Install Playwright
+        run: |
+          cd $HOME/install/test-app
+          npm install playwright
+          npx playwright install-deps
+          npx playwright install chromium
+      - name: Start dashboard and run tests
+        run: |
+          cd $HOME/install/test-app
+          # start the dev server in the background
+          npm run dev &
+          DEV_PID=$!
+          # Wait a moment for it to start
+          sleep 5
+          # Start the dashboard in the background
+          npx vite --port 5173 &
+          DASHBOARD_PID=$!
+          # Wait a moment for it to start
+          sleep 5
+          # Run the dashboard tests
+          node $GITHUB_WORKSPACE/.github/workflows/scripts/dashboard-tests.js
+          # Clean up dashboard process
+          kill $DASHBOARD_PID 2>/dev/null || true
+          # Clean up dev server process
+          kill $DEV_PID 2>/dev/null || true
+      - name: Upload dashboard test screenshots
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: dashboard-test-screenshots-${{ matrix.os }}-${{ matrix.node-version }}
+          path: /tmp/dashboard-test-*.png
+          retention-days: 28

+ 1 - 1
.github/workflows/publish_and_install_master.yml

@@ -7,7 +7,7 @@ on:
             - 'packages/**'
             - 'package.json'
             - 'package-lock.json'
-            
+
 defaults:
     run:
         shell: bash

+ 206 - 0
.github/workflows/scripts/dashboard-tests.js

@@ -0,0 +1,206 @@
+const { chromium } = require('playwright');
+
+/**
+ * This script is run as part of the "publish & install" workflow, and performs
+ * browser-based tests of the Vendure Dashboard to ensure it loads and login works.
+ */
+async function runDashboardTests() {
+    console.log('Starting dashboard tests...');
+
+    const browser = await chromium.launch({
+        headless: true,
+        args: ['--no-sandbox', '--disable-setuid-sandbox'],
+    });
+
+    let page;
+
+    try {
+        page = await browser.newPage();
+
+        // Navigate to the dashboard
+        console.log('Navigating to dashboard...');
+        await page.goto('http://localhost:5173');
+
+        // Wait for the page to load
+        await page.waitForLoadState('networkidle');
+
+        // Check if the login form is present
+        console.log('Checking for login form...');
+        const loginForm = await page.locator('form').first();
+        if (!loginForm) {
+            throw new Error('Login form not found');
+        }
+
+        // Fill in login credentials - try multiple selectors
+        console.log('Filling login credentials...');
+        const usernameInput = await page
+            .locator(
+                'input[name="username"], input[type="text"], input[placeholder*="username"], input[placeholder*="Username"]',
+            )
+            .first();
+        const passwordInput = await page
+            .locator(
+                'input[name="password"], input[type="password"], input[placeholder*="password"], input[placeholder*="Password"]',
+            )
+            .first();
+
+        if (!usernameInput || !passwordInput) {
+            throw new Error('Username or password input fields not found');
+        }
+
+        await usernameInput.fill('superadmin');
+        await passwordInput.fill('superadmin');
+
+        // Submit the form - try multiple selectors
+        console.log('Submitting login form...');
+        const submitButton = await page
+            .locator(
+                'button[type="submit"], input[type="submit"], button:has-text("Login"), button:has-text("Sign In")',
+            )
+            .first();
+        if (!submitButton) {
+            throw new Error('Submit button not found');
+        }
+        await submitButton.click();
+
+        // Wait for navigation after login
+        await page.waitForLoadState('networkidle');
+
+        // Check if we're logged in by looking for admin dashboard elements
+        console.log('Verifying successful login...');
+
+        // Wait for dashboard elements to appear with a more robust approach
+        const dashboardSelectors = ['h1:has-text("Dashboard")'];
+
+        // Try to wait for any dashboard element to appear
+        let dashboardElement = null;
+        let foundSelector = null;
+
+        for (const selector of dashboardSelectors) {
+            try {
+                // Wait up to 10 seconds for each selector
+                await page.waitForSelector(selector, { timeout: 10000 });
+                dashboardElement = await page.locator(selector).first();
+                if (dashboardElement && (await dashboardElement.count()) > 0) {
+                    foundSelector = selector;
+                    console.log(`Found dashboard element with selector: ${selector}`);
+                    break;
+                }
+            } catch (e) {
+                // Continue to next selector
+                console.log(`Selector ${selector} not found, trying next...`);
+            }
+        }
+
+        if (!foundSelector) {
+            // Fallback: check if we're still on a login page
+            const currentUrl = page.url();
+            const pageContent = await page.content();
+
+            if (
+                currentUrl.includes('login') ||
+                currentUrl.includes('auth') ||
+                pageContent.includes('login') ||
+                pageContent.includes('Login')
+            ) {
+                throw new Error('Login failed - still on login page');
+            }
+
+            // If we're not on login page, assume login was successful
+            console.log('Login appears successful - not on login page');
+        } else {
+            console.log('Dashboard loaded successfully');
+        }
+
+        // Take a screenshot for debugging
+        await page.screenshot({ path: '/tmp/dashboard-test-login.png' });
+        console.log('Screenshot saved to /tmp/dashboard-test-login.png');
+
+        // navigate to the product list page by clicking the "Products" link in the sidebar
+        // based on the text label "Products"
+        const productsLink = await page.locator('a:has-text("Products")').first();
+        if (!productsLink) {
+            throw new Error('Products link not found');
+        }
+        await productsLink.click();
+
+        // wait for the page to load and verify we're on the Products page
+        await page.waitForLoadState('networkidle');
+
+        // Wait for the Products page H1 to appear
+        console.log('Waiting for Products page to load...');
+        try {
+            await page.waitForSelector('h1:has-text("Products")', { timeout: 10000 });
+            console.log('Products page loaded successfully');
+        } catch (e) {
+            throw new Error('Products page did not load - H1 with "Products" text not found');
+        }
+
+        // get a list of the products in the product list page
+        const products = await page.locator('tbody tr').all();
+        console.log(`Found ${products.length} products`);
+
+        // Take a screenshot for debugging
+        await page.screenshot({ path: '/tmp/dashboard-test-products.png' });
+        console.log('Screenshot saved to /tmp/dashboard-test-products.png');
+
+        if (products.length !== 10) {
+            throw new Error('Expected 10 products, but found ' + products.length);
+        }
+
+        console.log('Dashboard tests passed!');
+    } catch (error) {
+        console.error('Dashboard test failed:', error.message);
+        // Take a screenshot on failure
+        try {
+            await page.screenshot({ path: '/tmp/dashboard-test-failure.png' });
+            console.log('Failure screenshot saved to /tmp/dashboard-test-failure.png');
+        } catch (screenshotError) {
+            console.log('Could not save failure screenshot:', screenshotError.message);
+        }
+        throw error;
+    } finally {
+        await browser.close();
+    }
+}
+
+// Wait for the dashboard to be available
+async function awaitDashboardStartup() {
+    console.log('Checking for availability of Dashboard...');
+    let attempts = 0;
+    const maxAttempts = 30;
+
+    while (attempts < maxAttempts) {
+        try {
+            const response = await fetch('http://localhost:5173');
+            if (response.ok) {
+                console.log('Dashboard is running!');
+                return;
+            }
+        } catch (e) {
+            // Ignore errors and continue polling
+        }
+
+        attempts++;
+        if (attempts < maxAttempts) {
+            console.log('Dashboard not yet available, waiting 2s...');
+            await new Promise(resolve => setTimeout(resolve, 2000));
+        }
+    }
+
+    throw new Error('Unable to establish connection to Dashboard server!');
+}
+
+// Main execution
+async function main() {
+    try {
+        await awaitDashboardStartup();
+        await runDashboardTests();
+        process.exit(0);
+    } catch (error) {
+        console.error('Dashboard tests failed:', error);
+        process.exit(1);
+    }
+}
+
+main();

+ 24 - 0
.github/workflows/scripts/vite.config.mts

@@ -0,0 +1,24 @@
+import { vendureDashboardPlugin } from '@vendure/dashboard/plugin';
+import { join, resolve } from 'path';
+import { pathToFileURL } from 'url';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+    build: {
+        outDir: join(__dirname, 'dist/dashboard'),
+    },
+    plugins: [
+        vendureDashboardPlugin({
+            vendureConfigPath: pathToFileURL('./src/vendure-config.ts'),
+            adminUiConfig: { apiHost: 'http://localhost', apiPort: 3000 },
+            gqlTadaOutputPath: './src/gql',
+        }),
+    ],
+    resolve: {
+        alias: {
+            // This allows all plugins to reference a shared set of
+            // GraphQL types.
+            '@/gql': resolve(__dirname, './src/gql/graphql.ts'),
+        },
+    },
+});