androidengineers.Book a session

Extend the validator with tests

Extend the validator with tests

exerciseSelf-paced

Learning outcome

Implement a real red-to-green policy change for independent checklist items.

Work on your learning branch. The goal is to reject duplicate checkbox data paths within one generated surface while still allowing the same path name in separate surfaces. Component IDs and data paths have different roles, so the existing duplicate-ID guard cannot enforce this policy. Write the failure before changing validation.

Append to server/protocol.test.mjs — expected to fail first

test('rejects two checklist items sharing a data path', () => {
  const reply = { text: 'Prepare', components: [
    { id: 'root', component: 'Column', children: ['a', 'b'] },
    { id: 'a', component: 'CheckBox', label: 'Pack charger', value: { path: '/ready0' } },
    { id: 'b', component: 'CheckBox', label: 'Plan travel', value: { path: '/ready0' } },
  ] };
  assert.throws(() => check(reply), /Duplicate checkbox path/);
});

test('allows independent checklist paths', () => {
  const reply = { text: 'Prepare', components: [
    { id: 'root', component: 'Column', children: ['a', 'b'] },
    { id: 'a', component: 'CheckBox', label: 'Pack charger', value: { path: '/ready0' } },
    { id: 'b', component: 'CheckBox', label: 'Plan travel', value: { path: '/ready1' } },
  ] };
  assert.deepEqual(check(reply).messages[1].updateDataModel.value,
    { ready0: false, ready1: false });
});

Run from a2ui/server

npm test

The first test should fail with a missing expected exception on the pinned baseline; the second should pass. If both pass before your implementation, confirm your checkout and test discovery. The existing check helper supplies an event registry and surface ID; neither fixture requires an external feed or Gemini call.

Solution: insert inside validateSurface

// Beside `const ids = new Set();`, before the component loop:
const checkboxPaths = new Set();

// Inside `for (const c of reply.components)`, immediately AFTER
// the existing CheckBox binding-format validation:
if (c.component === 'CheckBox') {
  if (checkboxPaths.has(c.value.path)) {
    throw Error('Duplicate checkbox path');
  }
  checkboxPaths.add(c.value.path);
}

Place the new set inside validateSurface, not at module scope: each surface needs an independent namespace. Place the policy after binding-format validation so malformed bindings fail before you dereference them. Rerun npm test: all 12 tests should now pass. The output check belongs on the server; adding a prompt sentence alone cannot enforce it.

Make the policy teachable

This sample chooses independent tasks. Shared bindings can be intentional in another application, so document this as PocketCommunity policy rather than an A2UI specification requirement. The extension belongs in your learner branch; the pinned production source is unchanged.

Practice and checkpoint

Keep the failing output, implementation diff and passing output in your learning log. Add a third test calling validateSurface twice with /ready0 to prove the set is scoped per request. Explain why changing component IDs would not fix shared binding state.

Source and next steps

Back to roadmap · Practice this unit in the codelab

Files to inspect

YOUR LEARNING JOURNEY

0 of 12 available lessons completed

Progress saved in this browser. No account needed.
Extend the validator with tests | A2UI on Android with PocketCommunity | Android Engineers