Initial release v1.0.0
Open-source marketplace for AI Agent skills. Features: - Next.js 15 web app with i18n (en/fa) - CLI tool for skill installation (npx skillhub) - GitHub crawler/indexer with multi-strategy discovery - Security scanning for all indexed skills - Self-hostable with Docker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
80
apps/web/e2e/mobile.spec.ts
Normal file
80
apps/web/e2e/mobile.spec.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Mobile Responsiveness', () => {
|
||||
test.use({ viewport: { width: 375, height: 667 } }); // iPhone SE size
|
||||
|
||||
test('should display mobile navigation', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
// Should have mobile menu button (hamburger)
|
||||
const mobileMenuBtn = page.locator('[data-testid="mobile-menu"], button[aria-label*="menu"], .hamburger, button:has(svg)').first();
|
||||
await expect(mobileMenuBtn).toBeVisible();
|
||||
});
|
||||
|
||||
test('should open mobile menu on click', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
const mobileMenuBtn = page.locator('[data-testid="mobile-menu"], button[aria-label*="menu"], .hamburger, header button:has(svg)').first();
|
||||
await mobileMenuBtn.click();
|
||||
|
||||
// Mobile menu should be visible with navigation links
|
||||
const mobileNav = page.locator('nav a, [data-testid="mobile-nav"] a').first();
|
||||
await expect(mobileNav).toBeVisible();
|
||||
});
|
||||
|
||||
test('should display skill cards in single column', async ({ page }) => {
|
||||
await page.goto('/browse');
|
||||
// Wait for skills to load
|
||||
await page.waitForSelector('a[href*="/skill/"], [data-testid="skill-card"]', { timeout: 10000 });
|
||||
|
||||
// Check that cards are stacked (single column layout)
|
||||
const cards = page.locator('[data-testid="skill-card"], article, .skill-card');
|
||||
const cardCount = await cards.count();
|
||||
if (cardCount >= 2) {
|
||||
const firstBox = await cards.first().boundingBox();
|
||||
const secondBox = await cards.nth(1).boundingBox();
|
||||
if (firstBox && secondBox) {
|
||||
// In single column, second card should be below first
|
||||
expect(secondBox.y).toBeGreaterThan(firstBox.y);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('should have readable text size on mobile', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
// Main heading should have reasonable font size for mobile
|
||||
const heading = page.locator('h1').first();
|
||||
const fontSize = await heading.evaluate((el) => {
|
||||
return window.getComputedStyle(el).fontSize;
|
||||
});
|
||||
const fontSizeNum = parseInt(fontSize);
|
||||
expect(fontSizeNum).toBeGreaterThanOrEqual(16); // At least 16px
|
||||
});
|
||||
|
||||
test('should have touch-friendly buttons', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
// Primary action buttons should be large enough for touch
|
||||
// Look for primary/main buttons (not small icon buttons)
|
||||
const primaryButtons = page.locator('.btn-primary, button.btn-primary, [role="button"].btn-primary');
|
||||
const buttonCount = await primaryButtons.count();
|
||||
|
||||
if (buttonCount > 0) {
|
||||
const firstButton = primaryButtons.first();
|
||||
const box = await firstButton.boundingBox();
|
||||
if (box) {
|
||||
// Primary buttons should be at least 32px height
|
||||
expect(box.height).toBeGreaterThanOrEqual(32);
|
||||
}
|
||||
} else {
|
||||
// If no primary buttons, check any visible button has reasonable minimum
|
||||
const allButtons = page.locator('button, a.btn, [role="button"]').filter({ hasText: /\S/ }); // buttons with text
|
||||
const anyButtonCount = await allButtons.count();
|
||||
if (anyButtonCount > 0) {
|
||||
const anyButton = allButtons.first();
|
||||
const box = await anyButton.boundingBox();
|
||||
if (box) {
|
||||
// Buttons with text should be at least 24px height (reasonable for mobile)
|
||||
expect(box.height).toBeGreaterThanOrEqual(24);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user