CtrlK
BlogDocsLog inGet started
Tessl Logo
PluginPublic

ai-unified-process/aiup-vaadin

AI Unified Process UI layer with Vaadin - Flow and Hilla views, Browserless and Vitest view tests, and Playwright end-to-end tests

94

1.08x
Quality

94%

Does it follow best practices?

Impact

94%

1.08x

Average score across 5 eval scenarios

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

UC-001-manage-persons.test.tsx.mdskills/hilla-ui-test/references/

UC-001-manage-persons.test.tsx

The source file UC-001-manage-persons.test.tsx, wrapped in Markdown so that every skill host installs it. Use the content of the code block below; a copy written to disk takes the name UC-<id>-<slug>.test.tsx of its use case, here UC-001-manage-persons.test.tsx, under src/main/frontend/tests/.

/**
 * Frontend tests for UC-001 "Manage Persons".
 *
 * Template for /hilla-ui-test suites:
 * - File name UC-XXX-<slug>.test.tsx, top-level describe named after the use case
 * - Generated endpoint clients mocked with vi.spyOn — no server, no network
 * - Mocked DTOs copy the field names of the generated TypeScript types
 * - The Grid's dataProvider calls the paged list(offset, limit, filter) and count(filter)
 * - Error flows reject with EndpointError so the production error path runs
 */
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import { EndpointError } from '@vaadin/hilla-frontend';
import ManagePersonsView from 'Frontend/views/persons';
import { PersonEndpoint } from 'Frontend/generated/endpoints';
import type PersonListItem from 'Frontend/generated/com/example/app/person/PersonListItem';

const alice: PersonListItem = { id: 1, firstName: 'Alice', lastName: 'Smith', email: 'alice@example.com' };
const bob: PersonListItem = { id: 2, firstName: 'Bob', lastName: 'Jones', email: 'bob@example.com' };

describe('UC-001: Manage Persons', () => {
  let listSpy: MockInstance;
  let countSpy: MockInstance;
  let saveSpy: MockInstance;

  beforeEach(() => {
    listSpy = vi.spyOn(PersonEndpoint, 'list').mockResolvedValue([alice, bob]);
    countSpy = vi.spyOn(PersonEndpoint, 'count').mockResolvedValue(2);
    saveSpy = vi
      .spyOn(PersonEndpoint, 'save')
      .mockImplementation(async (person) => ({ ...person, id: 3 }));
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('main scenario - lists persons on load', async () => {
    render(<ManagePersonsView />);

    await waitFor(() => expect(screen.getByText('alice@example.com')).to.exist);
    expect(screen.getByText('bob@example.com')).to.exist;
    expect(listSpy).toHaveBeenCalled();
    expect(countSpy).toHaveBeenCalled();
  });

  it('main scenario - saves a new person through the endpoint client', async () => {
    // The list the view reloads after the save — set before the click, so the reload cannot run first
    const carol: PersonListItem = { id: 3, firstName: 'Carol', lastName: 'Miller', email: 'carol@example.com' };
    render(<ManagePersonsView />);
    await waitFor(() => expect(screen.getByText('alice@example.com')).to.exist);
    listSpy.mockResolvedValue([alice, bob, carol]);
    countSpy.mockResolvedValue(3);

    await userEvent.type(screen.getByLabelText('First name'), 'Carol');
    await userEvent.type(screen.getByLabelText('Last name'), 'Miller');
    await userEvent.type(screen.getByLabelText('Email'), 'carol@example.com');
    await userEvent.click(screen.getByRole('button', { name: 'Save' }));

    await waitFor(() =>
      expect(saveSpy).toHaveBeenCalledWith(
        expect.objectContaining({
          firstName: 'Carol',
          lastName: 'Miller',
          email: 'carol@example.com',
        }),
      ),
    );
    // Step 5: the success message and the saved person in the refreshed list
    await waitFor(() => expect(screen.getByText('Person saved')).to.exist);
    await waitFor(() => expect(screen.getByText('carol@example.com')).to.exist);
  });

  it('A1: email already exists - shows the endpoint error to the user', async () => {
    saveSpy.mockRejectedValue(new EndpointError('Email already registered'));
    render(<ManagePersonsView />);

    await userEvent.type(screen.getByLabelText('First name'), 'Alice');
    await userEvent.type(screen.getByLabelText('Last name'), 'Smith');
    await userEvent.type(screen.getByLabelText('Email'), 'alice@example.com');
    await userEvent.click(screen.getByRole('button', { name: 'Save' }));

    await waitFor(() => expect(screen.getByText('Email already registered')).to.exist);
  });

  it('A2: required field missing - does not call the endpoint', async () => {
    render(<ManagePersonsView />);

    // Email left empty — useForm validation blocks submission client-side
    await userEvent.type(screen.getByLabelText('First name'), 'Carol');
    await userEvent.click(screen.getByRole('button', { name: 'Save' }));

    expect(saveSpy).not.toHaveBeenCalled();
  });
});

.mcp.json

README.md

tile.json