Usability audit

Scope and method

bin/test-example-order-management performs repeatable authoring exercises against a disposable FitNesse root. It adds a preferred-pricing boundary row, adds an inventory boundary row, selects a different customer and adds an expected order row to a query, creates a workflow page from existing operations, enters an invalid quantity, and changes expected price and order status values to force visible failures. The runner restores all edited pages and removes the temporary page even when an exercise fails.

These checks establish that the wiki edits execute and that FitNesse reports the resulting assertions. They are not a substitute for sessions with business analysts or other nontechnical authors. No such participants were available for this audit.

Role-based findings

Business analyst

Pricing and inventory boundaries are added as plain decision-table rows. The domain is expressed in business terms, and currency uses familiar decimal notation. A page can be extended without Perl edits when an existing fixture operation covers the behavior. The import setup and the required interposed words in script-table rows are FitNesse syntax details that need an example or author guidance.

QA analyst

Decision tables cover inclusive boundaries and rejected cases. Script checks assert business rejection as false plus a message rather than relying on an unexpected exception. Query expectations compare complete records and make unordered matching clear; the replenishment page uses an ordered query because priority order is meaningful. The runner verified that omitting an expected inventory row produces a query mismatch, while adding expected orders for another customer passes without fixture changes. Deliberately changing a pricing expectation produced one wrong assertion localized to the price cell. Changing the expected order status from submitted to reserved produced one wrong status assertion; restoring it returned the workflow to 11 right assertions.

Product owner

Page titles and prose state the behavior under review. A reader can follow a draft order through reservation and submission and see the expected status. The fixture still uses business identifiers such as SKU and customer ID; authors need the example's small data catalogue to interpret those values.

Perl maintainer

Pricing and store rules are independent of FitNesse. Fixtures are thin adapters with explicit method signatures. Query adapters translate an array of hash records into FitNesse query rows. This is a small amount of FitNesse-specific shaping, but it keeps Perl data structures out of wiki cells and avoids a demonstration-only hash-literal language. The wiki script phrases contain connector cells (quantity, to order) needed to form unambiguous fixture method names; that is the clearest usability friction found in the script interface.

Structured-data ergonomics

| Operation | Wiki form | Perl representation and adapter | Finding | |---|---|---|---| | Supply a simple record | Decision-table columns or script arguments | Fixture maps named cells to a hash-based domain record | Practical for flat records; authors do not write hash syntax | | Supply an order with items | One script action per item, with a generated order symbol | Order hash owns an array of line-item hashes | Readable for workflows; no nested literal is needed | | Return records | Query table with named headings | Array reference of hash references, mapped to [field, value] pairs | Natural in Perl; adapter has to translate to FitNesse's query shape | | Compare without row order | Query: | Query result rows matched as a set by FitNesse | Good for inventory and order records where display order is irrelevant | | Compare with row order | Ordered query: | Array order from the fixture is meaningful | Appropriate for replenishment priority | | Reuse generated value | $order FitNesse symbol in later script actions | String order identifier; no Perl reference crosses into the wiki | Straightforward and hides object identity | | Inspect an order field during a workflow | check status of order $order submitted or check total of order $order 18.00 | Fixture reads the order hash by its reused business identifier | Readable for the fields this workflow needs; Perl fields do not appear in the table | | Inspect fields in returned records | OrdersForCustomer query with named columns such as status and total | Query adapter maps an array of order hashes into FitNesse rows | Sufficient for displaying and matching several fields together | | Read any field from a returned Perl hash object | No generic field expression or access table | The order suite uses query rows and named fixture operations | Not supported generically; no current business case requires it | | Missing, null, and empty values | Blank query cells are wildcards; empty and null inputs are not interchangeable in the wiki | Perl distinguishes undef, '', and empty arrays, subject to FitNesse table semantics | The current suite avoids ambiguous blank cells; a dedicated authoring convention would be needed | | Invalid input | A table row supplies zero quantity and checks a rejection message | Fixture returns false and stores a domain-level message | Understandable when business-invalid values remain type-valid inputs |

Expression requirements

The examples below are concrete authoring needs, not a proposal to implement an expression engine in this project.

| Priority | Natural author expression | Need | Current behavior and minimum useful semantics | |---:|---|---|---| | 1 | quantity * unit price - discount + shipping | Recalculate an expected order total | FitNesse v20251025 evaluates backtick expressions through its Nashorn JavaScript integration on the FitNesse side; Perl SLiM preserves expression text. For money, an optional facility would need explicit decimal/integer-cent rules and checked arithmetic, not JavaScript floating point. | | 2 | $expectedTotal or $`order.total` | Compare an actual result with a previously stored result or a field | Ordinary symbol reuse is already supported. Field access uses Java-side expression evaluation; the smallest useful cross-language feature would be explicit named field extraction from declared record values, with missing-field behavior specified. | | 3 | $`lines[0].sku` | Check a particular line in a returned order | Java evaluates indexing and property/member access in FitNesse; Perl does not. This overlaps structured-record access and could be met by a narrowly defined table operation rather than general expressions. | | 4 | available >= requested && customerEligible | Express combined business conditions | Nashorn evaluates the current syntax in FitNesse. The demo instead reports the two business decisions through named fixture methods, which is more readable and avoids cross-backend coercion rules. |

The expression compatibility audit documents the Java implementation and its Java object semantics in detail. The order suite avoids expressions for its actual field checks: $order carries a readable business identifier into named fixture methods, and query tables show record fields under business headings. Scenarios reuse workflow steps but do not select record members. These existing forms are sufficient for the present business rules.

For example, the workflow checks two fields without exposing the order hash:

|check|status of order|$order|submitted|
|check|total of order|$order|18.00|

When several records and fields need inspection, the query presents the record fields as named columns:

|Query:orders for customer|C-100|
|order id|customer|status|total|
|ORD-022|C-100|submitted|23.00|

A generic accessor might instead require a table such as |check|field of record|$orderRecord|status|submitted|. That form is not currently supported and would require a raw hash value to survive callAndAssign. It would also need explicit missing-key and undefined-value rules. None of the nine business pages needs arbitrary key selection, so adding that operation would create a second way to express checks without reducing current wiki code. The evaluated result is therefore unchanged from the existing interface: named operations for workflow state and query columns for record collections. A future example with genuinely variable fields should first test a direct-key helper against those requirements; no general-purpose accessor belongs in the production distribution based on this example alone.

In this implementation, callAndAssign formats an ordinary hash result for FitNesse rather than retaining it as a raw record symbol. Existing object-valued symbols remain useful for values whose typed sequence contract retains a reference, but the order pages pass a business order ID instead. Changing symbol retention or mutating a fixture to stash returned hashes would add machinery solely to support an unused accessor. The disposable authoring exercise confirms the current status check fails at exactly one cell when its expected value changes and passes when restored.

Collection requirements

The suite needs arrays and hashes, both of which Perl already represents naturally. Unordered query matching is handled by FitNesse, and ordered priority results are represented by array order. Inventory membership uses ordinary domain lookups; order submission models FIFO or priority as a business rule rather than requiring Java Queue conversion. No Set, Queue, nested-generic, or additional collection protocol support was needed.

Conclusion

The suite provides a workable interface for flat business decisions, multi-step workflows, generated identifiers, and record queries. The disposable authoring exercise shows that a status expectation is localized to one wrong assertion and passes again when restored. Existing query and domain-specific fixture operations cover the fields required by this example; generic arbitrary-member access remains unavailable. The main observed syntax friction is script-table method-name construction and setup syntax. Human usability remains unverified until nontechnical authors use the pages.