DEV Community

Cover image for Fixing JavaScript console errors with Kiro and the Playwright MCP Server
iolaire mcfadden for kirodotdev

Posted on

Fixing JavaScript console errors with Kiro and the Playwright MCP Server

In 2022, I started the move to Mastodon on the Activity Pub platform. As I started in the community, people would advocate for including accessibility on the platform. In a good way, it was drummed into my head that every image posted should have ALT text added to the images to improve accessibility, especially for folks using screen readers. That’s something I'm happy to support, especially as my own eyes move to the reading glasses stage. I personally take issue with photographs of page-size documents where it’s very difficult to read the text zooming around an iPhone window.

I'm now most active on PixelFed at https://pixey.org/@iolaire. I enjoy posting photos and scrolling through everyone else's photos. However, I often am lazy or don't want to type the alt text on the phone. Luckily, Altbot would reply to my post with AI-generated captions. However, at some point, they implemented GDPR approval, and it no longer worked with PixelFed.

So this year, I started on a small project to backfill missing alt text on my PixelFed account. I wrote a nice small Flask script to generate image descriptions using llava:7b running on the Ollama server locally. When I say wrote, I started out with AI prompts in the various AI chat programs and then moved to using Amazon Q to polish it up.

However, on July 14th, Kiro from Amazon dropped providing a framework to do spec-driven development. As a getting stuff done person I had to try Kiro to practice a more organized and professional development style, and my 2,500-line Python script started growing quickly, its now over 200k lines:
√ Implement multi-user processing support
√ Enhance error handling and recovery
√ Optimize image processing
√ Optimize Ollama integration with llava:7b model
√ Improve database management
√ Enhance web interface
√ Improve ActivityPub integration
√ Implement system monitoring and management
√ Add comprehensive testing
√ Implement Multi-Platform ActivityPub Support
... and the list goes on

At some point, my specs included csrf-security-audit (don't add this mid-development, do it near the end), and I began frequent debugging using the JavaScript console. I've been learning a lot about CSRF tokens.

Pasting over JavaScript console errors into the chat for debugging became quite cumbersome. The Python tests Kiro wrote for me didn't actually catch frontend errors like CSRF token missmatches. So I went down the productive path of asking Kiro to write Playwright tests.

You can start to make progress on those CSRF token errors with commands like test the admin dashboard with playwright, use the admin user admin/abc134, look for JavaScript console errors, and resolve the issues.

Sometimes, to test redirects on pages, it’s great to let Kiro use the Playwright MCP server and step through the process.

Installing the MCP server is fairly simple once it’s on your system. In my case on a Mac Mini M4, it’s:
{
"mcpServers": {
"playwright": {
"command": "npx",
"args": [
"@playwright/mcp@latest"
],
"disabled": false,
"autoApprove": [
"browser_click",
"browser_wait_for",
"browser_navigate"
]
}
}
}

Note that a few steps are auto-approved; for most others, I like to run Playwright in headless mode = false and watch the test in case I need to give feedback to Kiro.

When running the tests via script, I've learned a few things, and Kiro helped to create a robust (maybe too robust) steering document - playwright-testing-guidelines.md. That steering document is at the end of the post.

Now, many spec tasks will do some visual testing with Playwright, but when it’s time to debug something, I'll often instruct Kiro to use the Playwright MCP to visually walk through the bug and review both the JavaScript console output and the app logs to identify and resolve the bug.

playwright-testing-guidelines.md steering document content:

Playwright Testing Guidelines

Mandatory Requirements

Timeouts (REQUIRED)

# Always prefix with timeout (run from tests/playwright/)
timeout 120 npx playwright test --config=0830_17_52_playwright.config.js
Enter fullscreen mode Exit fullscreen mode

Configuration Timeouts

module.exports = defineConfig({
  timeout: 120000,                    // 120 seconds
  expect: { timeout: 30000 },         // 30 seconds
  use: {
    headless: false,                  // MANDATORY for debugging
    actionTimeout: 30000,
    navigationTimeout: 60000
  },
  webServer: { timeout: 120000 }
});
Enter fullscreen mode Exit fullscreen mode

Authentication (MANDATORY)

const { ensureLoggedOut } = require('../utils/0830_17_52_auth_utils');

test.describe('Test Suite', () => {
  test.beforeEach(async ({ page }) => {
    await ensureLoggedOut(page);      // MANDATORY cleanup
  });

  test.afterEach(async ({ page }) => {
    await ensureLoggedOut(page);      // MANDATORY cleanup
  });
});
Enter fullscreen mode Exit fullscreen mode

File Organization

Directory Structure

tests/playwright/
├── tests/                           # All test files
├── utils/                           # Utility functions
├── 0830_17_52_playwright.config.js  # Configuration
├── 0830_17_52_package.json          # Dependencies
└── 0830_17_52_README.md             # Documentation
Enter fullscreen mode Exit fullscreen mode

File Naming (MANDATORY)

All files MUST use timestamp prefix: MMdd_HH_mm_filename.js

Browser Configuration

  • Headless: false (MANDATORY for debugging)
  • Primary: WebKit (Safari)
  • Secondary: Chromium, Firefox

Command Examples

# Navigate to correct directory first
cd tests/playwright

# Run tests
timeout 120 npx playwright test --config=0830_17_52_playwright.config.js

# Debug mode
timeout 120 npx playwright test --config=0830_17_52_playwright.config.js --debug

# Specific test
timeout 120 npx playwright test tests/0830_17_52_test_admin.js --timeout=120
Enter fullscreen mode Exit fullscreen mode

Landing Page Tests (NEW)

Running Landing Page Tests

# Navigate to correct directory first
cd tests/playwright

# Start web application first
python web_app.py & sleep 10

# Run accessibility tests (REQUIRED timeout prefix)
timeout 120 npx playwright test tests/0905_14_50_test_landing_page_accessibility.js --config=0830_17_52_playwright.config.js

# Run UI tests
timeout 120 npx playwright test tests/0905_14_50_test_landing_page_ui.js --config=0830_17_52_playwright.config.js

# Debug mode
timeout 120 npx playwright test tests/0905_14_50_test_landing_page_accessibility.js --config=0830_17_52_playwright.config.js --debug

# Use convenience script
./0905_14_50_run_landing_page_tests.sh --accessibility
./0905_14_50_run_landing_page_tests.sh --ui
Enter fullscreen mode Exit fullscreen mode

Critical Security Issues

Page.evaluate() Security Error

Problem: SecurityError: The operation is insecure in WebKit

Solution: Avoid page.evaluate() for storage cleanup

// CORRECT - Safe cleanup
test.beforeEach(async ({ page }) => {
  await page.context().clearCookies();
});

// WRONG - Causes SecurityError
test.beforeEach(async ({ page }) => {
  await page.evaluate(() => {
    localStorage.clear();    // SecurityError!
  });
});
Enter fullscreen mode Exit fullscreen mode

Navigation Timeout Prevention

Problem: networkidle timeouts with WebSockets

Solution: Use domcontentloaded

// CORRECT
await page.goto('/login', { 
  waitUntil: 'domcontentloaded',
  timeout: 30000 
});

// WRONG - Times out
await page.goto('/login', { 
  waitUntil: 'networkidle'
});
Enter fullscreen mode Exit fullscreen mode

Quality Standards

  • All tests must pass consistently
  • No console errors (WebSocket, CORS, notifications)
  • Proper cleanup after each test
  • Clear, descriptive test names

Generic Test Running Instructions (For Any Playwright Test)

Standard Test Execution Pattern

# 1. ALWAYS navigate to correct directory first
cd tests/playwright

# 2. Start web application if needed (most tests require this)
python web_app.py & sleep 10

# 3. Verify app is running
curl -s http://127.0.0.1:5000 | head -5

# 4. Run test with MANDATORY timeout prefix and working config
timeout 120 npx playwright test tests/[TIMESTAMP]_test_[NAME].js --config=0830_17_52_playwright.config.js

# 5. For debugging, add --debug flag
timeout 120 npx playwright test tests/[TIMESTAMP]_test_[NAME].js --config=0830_17_52_playwright.config.js --debug
Enter fullscreen mode Exit fullscreen mode

Test File Pattern Recognition

  • All test files follow: tests/MMdd_HH_mm_test_[description].js
  • Always use existing working config: 0830_17_52_playwright.config.js
  • Always use timeout prefix: timeout 120
  • Always run from tests/playwright/ directory

Universal Prerequisites Checklist

  1. ✅ Navigate to tests/playwright/ directory
  2. ✅ Start web application: python web_app.py & sleep 10
  3. ✅ Verify app responds: curl http://127.0.0.1:5000
  4. ✅ Use timeout prefix: timeout 120
  5. ✅ Use working config: --config=0830_17_52_playwright.config.js
  6. ✅ For debugging: add --debug flag

Common Error Prevention

  • "No tests found": Check file path and use correct config
  • Timeout errors: Ensure web app is running first
  • Navigation errors: Use domcontentloaded not networkidle
  • Security errors: Avoid page.evaluate() for storage operations
  • Browser errors: Ensure browsers installed: npx playwright install

Troubleshooting

  1. Verify web app running on http://127.0.0.1:5000
  2. Confirm admin/user accounts exist
  3. Check browser console for errors
  4. Use debug mode for step-by-step execution

AJAX Form Testing (Lessons Learned)

JavaScript Event Handling

Problem: JavaScript event listeners may not be properly attached or executed in Playwright.

Solution: Use direct element interaction and verify JavaScript execution:

// CORRECT - Direct button click with proper waiting
await page.click('button[type="submit"]');
await page.wait_for_timeout(5000); // Wait for AJAX completion

// ALSO CORRECT - Verify JavaScript is executing
const js_event_listener = await page.evaluate('''
    () => {
        const form = document.querySelector('#edit-mode form');
        if (form) {
            return form.onsubmit !== null;
        }
        return false;
    }
''');
console.log(`JavaScript event listener attached: ${js_event_listener}`);
Enter fullscreen mode Exit fullscreen mode

Console Message Monitoring

Problem: JavaScript errors and AJAX responses may not be visible in test output.

Solution: Set up real-time console message capture:

// CORRECT - Real-time console monitoring
console_messages = [];
def console_handler(msg):
    message = f"Console {msg.type}: {msg.text}"
    console_messages.append(message);
    print(f"   {message}");
page.on("console", console_handler);
Enter fullscreen mode Exit fullscreen mode

AJAX Form Submission Testing

Problem: Forms using AJAX may not trigger traditional form submission events.

Solution: Test both direct interaction and verify AJAX responses:

// CORRECT - Test AJAX form submission
await page.fill('#first_name', 'TestValue');
await page.click('button[type="submit"]');

// Wait for AJAX completion
await page.wait_for_timeout(5000);

// Verify success through console logs
if any('Response data: {success: true}' in msg for msg in console_messages):
    console.log("✅ AJAX submission successful");
Enter fullscreen mode Exit fullscreen mode

Page Reload Handling

Problem: AJAX forms that reload pages may cause timeout issues.

Solution: Use appropriate timeouts and verify page state:

// CORRECT - Handle page reloads after AJAX
await page.wait_for_timeout(5000); // Wait for reload
await page.wait_for_selector('#view-mode', state='visible', timeout=30000);

// Alternative - Check URL if page reloads
current_url = page.url;
if current_url.includes('/profile'):
    console.log("✅ Page reload successful");
Enter fullscreen mode Exit fullscreen mode

Form Validation Testing

Problem: CSRF protection and form validation may fail silently.

Solution: Verify form data and CSRF tokens:

// CORRECT - Check form structure and CSRF
form_html = await page.inner_html('#edit-mode form');
console.log(`Form contains CSRF token: ${'csrf_token' in form_html}`);

// Verify form data submission
console.log("Form data being submitted:");
for (let [key, value] of formData.entries()) {
    console.log(`${key}: ${value}`);
}
Enter fullscreen mode Exit fullscreen mode

Browser-Specific Behavior

Problem: Different browsers may handle JavaScript and AJAX differently.

Solution: Test across browsers and use appropriate waits:

// CORRECT - Browser-agnostic testing
const browsers = ['webkit', 'chromium', 'firefox'];
for (const browser of browsers) {
    const context = await browser.newContext();
    const page = await context.newPage();
    // Test implementation
    await browser.close();
}
Enter fullscreen mode Exit fullscreen mode

Debug Mode Best Practices

Problem: Headless mode may hide JavaScript and AJAX issues.

Solution: Always use headless mode for debugging:

// CORRECT - Debug configuration
const browser = await p.webkit.launch(headless=false); // MANDATORY
const context = await browser.newContext();
const page = await context.newPage();

// Add screenshots for debugging
await page.screenshot(path='debug_result.png');
Enter fullscreen mode Exit fullscreen mode

Test Organization for AJAX Features

Problem: AJAX functionality requires comprehensive testing of both client and server sides.

Solution: Create dedicated test files for AJAX features:

tests/playwright/
├── test_profile_editing_functionality.py  # AJAX form tests
├── debug_profile_submission.py            # Debug scripts
└── utils/
    └── ajax_helpers.py                    # AJAX test utilities
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
onlineproxy profile image
OnlineProxy

When you're testing with Playwright, you’ll run into some classic JavaScript console errors that can trip you up. Stuff like network errors, type errors from trying to use something that ain’t defined, CORS drama when you're crossing domains without permission, and those lovely uncaught exceptions when your DOM elements ghost you. Now, if you're rolling with the Playwright MCP server, you’re in luck - it cranks up the efficiency by letting you run tests in parallel across multiple setups, cuts down that wait time, and gives you a central log hub to untangle bugs without losing your mind. CSRF issues can be sneaky here, so keep an eye on those network requests and double-check how your tokens are being handled in the session.