DEV Community

Enis Kovac
Enis Kovac

Posted on

What If You Could Test Your Web App Just by Describing What to Test?

That's the question we kept coming back to when building Lama.
Enter fullscreen mode Exit fullscreen mode

Not "how do we make test scripting faster?" Not "how do we reduce selector fragility?" Those are symptoms. The real question was simpler: why does testing still require you to think like a machine when you already know exactly what you want to verify?

You know the checkout flow needs to work. You know the login should reject bad credentials. You know the returns flow should redirect correctly. That knowledge exists in your head right now — in plain English.

The gap between knowing what to test and having a test that proves it works — that's what we built Lama to close.


How It Works

Lama is an AI-powered IDE for testing. You open it, type what you want to test in plain English, and the agent takes over.

You type:

Test the checkout flow.
Enter fullscreen mode Exit fullscreen mode

Lama opens a real browser, navigates to your application, and works through it the way a human tester would — clicking buttons, filling forms, verifying pages load correctly, checking that the right things appear after each action. Every browser interaction surfaces as a tool call in the chat panel alongside the agent's reasoning, so you can follow exactly what it's doing and why.

Once it has explored the flow, Lama writes a production-ready test file. Not a proprietary script. Not a recorded macro. A real checkout.spec.ts in Playwright:

import { test, expect } from '@playwright/test';

test.describe('Checkout flow', () => {

  test.beforeEach(async ({ page }) => {
    // Log in and seed cart before each test
    await page.goto('/login');
    await page.getByLabel('Email').fill('testuser@acme.com');
    await page.getByLabel('Password').fill('s3cureP@ss');
    await page.getByRole('button', { name: 'Sign in' }).click();
    await expect(page).toHaveURL(/dashboard/);

    // Add item to cart via API for speed
    await page.request.post('/api/cart/items', {
      data: { productId: 'prod_abc123', quantity: 2 }
    });
  });

  test('completes checkout with valid card and verifies order', async ({ page }) => {
    await page.goto('/cart');
    await expect(page.getByTestId('cart-item-count')).toHaveText('2');

    await page.getByRole('button', { name: 'Proceed to checkout' }).click();
    await expect(page).toHaveURL(/checkout/);

    // Fill shipping details
    await page.getByLabel('Full name').fill('Jane Smith');
    await page.getByLabel('Address').fill('123 Main St');
    await page.getByLabel('City').fill('New York');
    await page.getByLabel('ZIP code').fill('10001');
    await page.getByRole('button', { name: 'Continue to payment' }).click();

    // Fill payment details inside Stripe iframe
    const stripeFrame = page.frameLocator('iframe[name="stripe-card"]');
    await stripeFrame.getByPlaceholder('Card number').fill('4242424242424242');
    await stripeFrame.getByPlaceholder('MM / YY').fill('12/26');
    await stripeFrame.getByPlaceholder('CVC').fill('123');

    await page.getByRole('button', { name: 'Pay now' }).click();

    // Wait for redirect and verify confirmation
    await expect(page).toHaveURL(/order-confirmation/);
    await expect(page.getByText('Order confirmed')).toBeVisible();
    await expect(page.getByTestId('order-id')).not.toBeEmpty();

    // Verify confirmation email trigger (mocked)
    const emailRequest = page.waitForRequest(req =>
      req.url().includes('/api/notifications/email') && req.method() === 'POST'
    );
    await expect(emailRequest).resolves.toBeTruthy();
  });

  test('shows error for declined card and stays on payment page', async ({ page }) => {
    await page.goto('/checkout');
    const stripeFrame = page.frameLocator('iframe[name="stripe-card"]');
    await stripeFrame.getByPlaceholder('Card number').fill('4000000000000002'); // Stripe decline card
    await stripeFrame.getByPlaceholder('MM / YY').fill('12/26');
    await stripeFrame.getByPlaceholder('CVC').fill('123');

    await page.getByRole('button', { name: 'Pay now' }).click();

    await expect(page.getByText('Your card was declined')).toBeVisible();
    await expect(page).toHaveURL(/checkout/);
  });

});
Enter fullscreen mode Exit fullscreen mode

Standard Playwright. No wrapper. No proprietary format. It lives in your repo and runs in your CI like any other test your team wrote.

You then ask it to test another flow:

Now test the returns flow.
Enter fullscreen mode Exit fullscreen mode

Same process. A returns.spec.ts appears in the editor.

No recording sessions. No selector hunting. No boilerplate.


Beyond Test Generation

Writing the tests is only part of it.

Once you have test files, you can organize them into groups through a simple context menu — by feature, sprint, priority, or however your team thinks. The Tests panel gives you a flat view of everything, with a single "Run All" button.

When you run them, you watch the queue move in real time — each test cycling through queued → running → passed. A results summary bar at the end shows what passed and what needs attention.

It's a complete testing workspace: write, organize, run, review — without leaving the app.


What We're Not Claiming

Lama is in research preview. It's genuinely useful today for standard web application testing and generates accurate, runnable tests across a wide range of flows.

But it's not magic, and we'd rather be honest about the edges:

  • Works best on standard web flows — forms, navigation, state changes
  • Mobile testing is not supported yet
  • Visual regression testing is not yet included

We're building in public and iterating on real feedback. The research preview exists precisely so we can learn from people using it on real applications — not demo apps.


Why This Matters to Developers

The average QA engineer spends a significant chunk of their week not actually testing — but writing and maintaining the infrastructure that enables testing. That overhead is why test coverage feels expensive. That's why teams skip sprints. That's why "we'll do it next week" keeps getting said.

And for developers who want to verify their own implementation — not hand it off to QA — the barrier of setting up a full test suite often makes it easier to just ship and hope.

Lama doesn't solve deadline pressure or org culture. But it does make the mechanical part — turning what you know into working test code — fast enough that the friction largely disappears.

When writing a test takes two minutes instead of two hours, the decision to skip it stops making sense.


Try It

Free to use. Top up AI credits as you go — no subscription, no monthly commitment.

If you're a QA engineer, a developer wanting to verify your own work, or a founder who can't justify a dedicated QA hire yet — we'd genuinely love your feedback.

🔗 lamaqa.com

Honest feedback — positive or negative — is exactly what the research preview is for.

Top comments (0)