Skip to main content
Version: 1.x

Vocabulary Discovery

Demonstrates Praman's vocabulary system from playwright-praman/vocabulary: domain loading, fuzzy term search, confidence scoring, field selector resolution, autocomplete suggestions, and integration with the intent API.

SAP Domain Coverage​

The vocabulary system ships with 6 SAP domain JSON files:

DomainSAP ModuleCoverage
procurementMMVendors, POs, goods receipts
salesSDCustomers, sales orders, deliveries
financeFIGL accounts, AP, AR
manufacturingPPProduction orders, BOMs, routings
warehouseWM/EWMStorage locations, picking
qualityQMInspection lots, quality notifications

Source​

import { test, expect } from 'playwright-praman';
import { createVocabularyService } from 'playwright-praman/vocabulary';
import type { VocabularyService } from 'playwright-praman/vocabulary';
import { fillField } from 'playwright-praman/intents';

test.describe('Vocabulary Discovery', () => {
let vocab: VocabularyService;

test.beforeEach(() => {
vocab = createVocabularyService();
});

test('load domains and search terms', async () => {
await test.step('Load SAP procurement domain', async () => {
await vocab.loadDomain('procurement');
const stats = vocab.getStats();
expect(stats.loadedDomains).toContain('procurement');
expect(stats.totalTerms).toBeGreaterThan(0);
});

await test.step('Load multiple domains', async () => {
await vocab.loadDomain('sales');
await vocab.loadDomain('finance');
const stats = vocab.getStats();
expect(stats.loadedDomains).toContain('procurement');
expect(stats.loadedDomains).toContain('sales');
expect(stats.loadedDomains).toContain('finance');
});

await test.step('Search across all domains', async () => {
const results = await vocab.search('vendor');
expect(results.length).toBeGreaterThan(0);
expect(results[0]!.confidence).toBeGreaterThanOrEqual(0.85);
});

await test.step('Search within a specific domain', async () => {
const results = await vocab.search('vendor', 'procurement');
for (const result of results) {
expect(result.domain).toBe('procurement');
}
});
});

test('fuzzy matching handles typos and partial terms', async () => {
await test.step('Load domain', async () => {
await vocab.loadDomain('procurement');
});

await test.step('Exact match (confidence = 1.0)', async () => {
const results = await vocab.search('supplier');
const exact = results.find((r) => r.confidence === 1.0);
expect(exact).toBeTruthy();
});

await test.step('Synonym match (confidence = 1.0)', async () => {
const results = await vocab.search('vendor');
const match = results.find((r) => r.confidence === 1.0);
expect(match).toBeTruthy();
});

await test.step('Prefix match (confidence = 0.9)', async () => {
const results = await vocab.search('sup');
const prefix = results.find((r) => r.confidence === 0.9);
expect(prefix).toBeTruthy();
});

await test.step('Fuzzy match with typo (confidence = 0.5)', async () => {
// "vendro" is within Levenshtein distance 2 of "vendor"
const results = await vocab.search('vendro');
const fuzzy = results.find((r) => r.confidence >= 0.5);
expect(fuzzy).toBeTruthy();
});

await test.step('No match for unrelated terms', async () => {
const results = await vocab.search('xyznonexistent');
expect(results.length).toBe(0);
});
});

test('resolve field selectors from business terms', async () => {
await test.step('Load domain', async () => {
await vocab.loadDomain('procurement');
});

await test.step('High-confidence term resolves to UI5 selector', async () => {
const selector = await vocab.getFieldSelector('vendor', 'procurement');
expect(selector).toBeTruthy();
});

await test.step('Ambiguous terms return undefined', async () => {
// "number" could match PO number, material number, vendor number
const selector = await vocab.getFieldSelector('number', 'procurement');
// Returns undefined rather than guessing
});

await test.step('SAP ABAP field names resolve via synonyms', async () => {
const results = await vocab.search('LIFNR');
const match = results.find((r) => r.sapField === 'LIFNR');
expect(match).toBeTruthy();
});
});

test('suggestions for autocomplete', async () => {
await test.step('Load domains', async () => {
await vocab.loadDomain('procurement');
await vocab.loadDomain('sales');
});

await test.step('Get suggestions for partial input', async () => {
const suggestions = await vocab.getSuggestions('pur');
expect(suggestions.length).toBeGreaterThan(0);
});

await test.step('Limit suggestion count', async () => {
const top3 = await vocab.getSuggestions('mat', 3);
expect(top3.length).toBeLessThanOrEqual(3);
});
});

test('vocabulary-driven field filling in a live SAP app', async ({ ui5, ui5Navigation }) => {
await test.step('Load vocabulary and navigate', async () => {
await vocab.loadDomain('procurement');
await ui5Navigation.navigateToApp('PurchaseOrder-create');
await ui5.waitForUI5();
});

await test.step('Fill fields using vocabulary terms', async () => {
const vendorResult = await fillField(ui5, vocab, 'Vendor', '100001', {
domain: 'procurement',
});
expect(vendorResult.status).toBe('success');

const materialResult = await fillField(ui5, vocab, 'Material', 'RAW-0001', {
domain: 'procurement',
});
expect(materialResult.status).toBe('success');

const quantityResult = await fillField(ui5, vocab, 'Order Quantity', '100', {
domain: 'procurement',
});
expect(quantityResult.status).toBe('success');
});

await test.step('Verify cache statistics', async () => {
const stats = vocab.getStats();
// After lookups, cache should have hits
});
});
});

Key Concepts​

Match Confidence Tiers​

TierConfidenceCriteria
Exact1.0Query matches term name or synonym (case-insensitive)
Prefix0.9Term name starts with the query string
Partial0.7Term name contains the query string
Fuzzy0.5Levenshtein distance <= 3 characters

VocabularyService API​

  • createVocabularyService() -- factory function, returns a new service instance
  • svc.loadDomain(domain) -- lazily loads a SAP domain JSON file
  • svc.search(query, domain?) -- searches across loaded domains, returns VocabularySearchResult[] sorted by confidence
  • svc.getFieldSelector(term, domain?) -- resolves a high-confidence term to a UI5 selector (returns undefined if ambiguous)
  • svc.getSuggestions(prefix, limit?) -- autocomplete suggestions for partial input
  • svc.getStats() -- returns loaded domains, total terms, cache hits/misses

Disambiguation​

When getFieldSelector() encounters multiple matches scoring between 0.7 and 0.85 without a clear winner, it returns undefined rather than guessing. This allows the caller (or an AI agent) to present options to the user.

ABAP Field Name Support​

SAP technical field names (e.g., LIFNR, MATNR, BUKRS) are included as synonyms in the domain JSON files, allowing resolution from either business terms or ABAP names.