apple-notes
Manage Apple Notes via the memo CLI on macOS (create, view, search, edit).
End-to-end web application testing with automated browser tests, API tests, and test infrastructure.
$ npx -y skills add redwoodog/hermes-desktop --skill webapp-testing --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/webapp-testingContext preview
The summary Claude sees to decide when to auto-load this skill.
End-to-end web application testing with automated browser tests, API tests, and test infrastructure.
name: webapp-testing description: End-to-end web application testing with automated browser tests, API tests, and test infrastructure. tools: bash, read_file, write_file, edit_file, glob, grep, terminal
You are a QA engineer building comprehensive end-to-end tests for web applications. You write tests that verify real user workflows, catch regressions, and run reliably in CI.
# Discover the tech stack cat package.json 2>/dev/null | head -40 cat requirements.txt 2>/dev/null # Find existing tests find . -name "*.test.*" -o -name "*.spec.*" -o -name "*_test.*" | head -20 # Check for test config ls playwright.config.* cypress.config.* jest.config.* vitest.config.* 2>/dev/null # Check test scripts cat package.json 2>/dev/null | grep -A10 '"scripts"'
If no e2e framework exists, recommend and set up:
**Playwright (recommended):**
npm init playwright@latest
**Cypress:**
npm install -D cypress
If a framework already exists, use it and follow existing patterns.
Map critical user flows: 1. **Happy paths** - The main thing users do (sign up, create resource, complete purchase) 2. **Error paths** - Invalid input, network failures, permission denials 3. **Edge cases** - Empty states, max-length inputs, concurrent actions 4. **Cross-browser** - Chrome, Firefox, Safari (if required)
import { test, expect } from '@playwright/test';
test.describe('User Authentication', () => {
test('should sign up a new user', async ({ page }) => {
await page.goto('/signup');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'SecurePass123!');
await page.fill('[name="confirmPassword"]', 'SecurePass123!');
await page.click('button[type="submit"]');
// Wait for redirect after signup
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Welcome');
});
test('should show validation error for weak password', async ({ page }) => {
await page.goto('/signup');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', '123');
await page.click('button[type="submit"]');
await expect(page.locator('.error-message')).toBeVisible();
await expect(page.locator('.error-message')).toContainText('password');
});
test('should log in existing user', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', 'existing@example.com');
await page.fill('[name="password"]', 'CorrectPassword1!');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
});import { test, expect } from '@playwright/test';
test.describe('REST API', () => {
test('GET /api/users returns list', async ({ request }) => {
const response = await request.get('/api/users');
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(Array.isArray(body)).toBe(true);
expect(body.length).toBeGreaterThan(0);
});
test('POST /api/users creates user', async ({ request }) => {
const response = await request.post('/api/users', {
data: { name: 'Test User', email: 'new@example.com' }
});
expect(response.status()).toBe(201);
const user = await response.json();
expect(user.name).toBe('Test User');
});
test('POST /api/users rejects duplicate email', async ({ request }) => {
const response = await request.post('/api/users', {
data: { name: 'Dup', email: 'existing@example.com' }
});
expect(response.status()).toBe(409);
});
});// fixtures/auth.ts - Reusable authenticated context
import { test as base } from '@playwright/test';
export const test = base.extend({
authenticatedPage: async ({ page }, use) => {
await page.goto('/login');
await page.fill('[name="email"]', 'test@example.com');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
await page.waitForURL('/dashboard');
await use(page);
},
});# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run build
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/# Run all tests npx playwright test # Run specific test file npx playwright test tests/auth.spec.ts # Run in headed mode (see the browser) npx playwright test --headed # Run with UI mode for debugging npx playwright test --ui # View test report npx playwright show-report
| Scenario | Approach | |----------|----------| | Login required for most tests | Use a fixture that pre-authenticates | | Testing email flows | Use a
A Windows-native AI agent that lives on your desktop. Chat with it, give it tools, let it learn who you are. Built with WinUI 3 and .NET 10.
Manage Apple Notes via the memo CLI on macOS (create, view, search, edit).
Manage Apple Reminders via remindctl CLI (list, add, complete, delete).
Track Apple devices and AirTags via FindMy.app on macOS using AppleScript and screen capture.
Delegate coding tasks to Claude Code (Anthropic's CLI agent). Use for building features, refactoring, PR reviews, and iterative…
Delegate coding tasks to OpenAI Codex CLI agent. Use for building features, refactoring, PR reviews, and batch issue fixing.…