Skip to content

Commit

Permalink
scenario: update tests for indexed-repeat support
Browse files Browse the repository at this point in the history
- Direct test of function implementation in form context has minor modification to omit superfluous answer expression position predicates (and PORTING NOTES updated accordingly)

- Child Vaccination smoke test updates to reflect progress based on ability to evaluate `indexed-repeat` function call. It is now blocked on lack of support for repeats with `jr:count`. It seems likely that functionality will allow significantly more progress through this smoke test.
  • Loading branch information
eyelidlessness committed Jun 27, 2024
1 parent 56df0ea commit 66aa8c9
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 48 deletions.
20 changes: 14 additions & 6 deletions packages/scenario/test/repeat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2262,15 +2262,18 @@ describe('Tests ported from JavaRosa - repeats', () => {
/**
* **PORTING NOTES**
*
* - Fails pending implementation of `indexed-repeat` XPath function.
*
* - Parameters adapted to match values in JavaRosa. Note that the
* parameters are passed as {@link options} rather than destructured. Java
* lets you reference `group` (the class property) and `group` (the
* imported static method) in the same scope. TypeScript/JavaScript don't
* let you do that... which is fine, because doing that is really weird!
*
* - `answer` calls updated to omit superfluous position predicate on
* the non-repeat `some-group` step (we do this lookup by `reference`,
* not evaluating arbitrary XPath expressions to identify the question
* being answered).
*/
it.fails.each<IndexedRepeatRelativeRefsOptions>(parameters)('$testName', async (options) => {
it.each<IndexedRepeatRelativeRefsOptions>(parameters)('$testName', async (options) => {
const scenario = await Scenario.init(
'Some form',
html(
Expand Down Expand Up @@ -2311,9 +2314,14 @@ describe('Tests ported from JavaRosa - repeats', () => {
)
);

scenario.answer('/data/some-group[1]/item[1]/value', 11);
scenario.answer('/data/some-group[1]/item[2]/value', 22);
scenario.answer('/data/some-group[1]/item[3]/value', 33);
// scenario.answer('/data/some-group[1]/item[1]/value', 11);
scenario.answer('/data/some-group/item[1]/value', 11);

// scenario.answer('/data/some-group[1]/item[2]/value', 22);
scenario.answer('/data/some-group/item[2]/value', 22);

// scenario.answer('/data/some-group[1]/item[3]/value', 33);
scenario.answer('/data/some-group/item[3]/value', 33);

expect(scenario.answerOf('/data/total-items')).toEqualAnswer(intAnswer(3));
expect(scenario.answerOf('/data/some-group/last-value')).toEqualAnswer(intAnswer(33));
Expand Down
117 changes: 75 additions & 42 deletions packages/scenario/test/smoketests/child-vaccination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,22 @@ import type {
} from '../../src/jr/event/getPositionalEvents.ts';
import { JRTreeReference as BaseJRTreeReference } from '../../src/jr/xpath/JRTreeReference.ts';

/**
* Most naive approach first: strip numeric positional predicates, ignoring any
* syntax ambiguities.
*
* If we need to do anything more complex than that, we'd likely benefit from
* pulling in the XPath parser to introduce syntax-based analysis and
* serialization. That's not the best investment for this smoke test, but it
* will have broad applicability for a variety of engine work that we've
* deferred, putting another figurative bird in that figurative stone's
* trajectory. (Plus there is considerable WIP that we could draw from if we
* need/wish to do that.)
*/
const naiveStripPositionalPredicates = (expression: string): string => {
return expression.replaceAll(/\[\d+\]/g, '');
};

/**
* **PORTING NOTES**
*
Expand Down Expand Up @@ -54,7 +70,9 @@ const refSingletons = new UpsertableMap<string, JRTreeReference>();

class JRTreeReference extends BaseJRTreeReference {
genericize(): JRTreeReference {
throw new IncompleteTestPortError('TreeReference.genericize');
const reference = naiveStripPositionalPredicates(this.xpathReference);

return new JRTreeReference(reference);
}

equals(other: JRTreeReference): boolean {
Expand Down Expand Up @@ -123,6 +141,24 @@ class Scenario extends BaseScenario {
/* eslint-enable no-console */
}

private getNextEventPosition(): AnyPositionalEvent {
const currentPosition = this.getSelectedPositionalEvent();

if (currentPosition.eventType === 'END_OF_FORM') {
throw 'todo';
}

const events = this.getPositionalEvents();
const position = this.getEventPosition();
const next = events[position + 1];

if (next == null) {
throw new Error(`No question at position: ${position}`);
}

return next;
}

/**
* @deprecated
*
Expand All @@ -142,7 +178,13 @@ class Scenario extends BaseScenario {
* increased clarity of intent.
*/
nextRef(): JRTreeReference {
throw new IncompleteTestPortError('Extended Scenario.nextRef');
const next = this.getNextEventPosition();

if (next.eventType === 'END_OF_FORM') {
throw 'todo';
}

return new JRTreeReference(next.node.currentState.reference);
}

/**
Expand Down Expand Up @@ -549,42 +591,32 @@ describe('ChildVaccinationTest.java', () => {
});

it.fails('[smoke test]', async () => {
let scenario: Scenario | null = null;

try {
scenario = await Scenario.init('child_vaccination_VOL_tool_v12.xml');
const scenario = await Scenario.init('child_vaccination_VOL_tool_v12.xml');

expect.fail('Update `child-vaccination.test.ts`, known failure mode has changed');
} catch (error) {
expect(error).toBeInstanceOf(Error);
scenario.next('/data/building_type');
scenario.answer('multi');
scenario.next('/data/not_single');
scenario.next('/data/not_single/gps');
scenario.answer('1.234 5.678');
scenario.next('/data/building_name');
scenario.answer('Some building');
scenario.next('/data/full_address1');
scenario.answer('Some address, some location');

// Failure of this assertion likely means that we've implemented the
// `indexed-repeat` XPath function. When that occurs, these error
// condition assertions should be removed, and the `Scenario.init` call
// should be treated normally.
//
// If a new failure occurs after that point, and that failure cannot be
// addressed by updating the test to be more complete (e.g. by specifying
// more of the expected references in `scenario.next` calls, or
// implementing other aspects of the test which are currently deferred),
// consider adding a similar function/assertion/comment to this effect,
// asserting that new known failure condition and prompting the test to be
// updated again once it is resolved.
expect((error as Error).message).toContain('function not defined: indexed-repeat');
}
// endregion

if (scenario == null) {
return;
}
const currentExpectedPointOfFailure = () => {
expect(scenario.answerOf('/data/household_count').toString()).toBe('2');

scenario.next('/data/building_type');
const secondHouseholdControlledByRepeatCount = scenario.getInstanceNode('/data/household[2]');

const currentExpectedPointOfFailure = () => {
scenario.answer('multi');
expect(secondHouseholdControlledByRepeatCount).not.toBeNull();
};

try {
expect(currentExpectedPointOfFailure).toThrowError('function not defined: indexed-repeat');
expect(currentExpectedPointOfFailure).toThrowError(
'No instance node for reference: /data/household[2]'
);

if (typeof currentExpectedPointOfFailure === 'function') {
scenario.trace(
Expand All @@ -593,20 +625,21 @@ describe('ChildVaccinationTest.java', () => {

return;
}
} catch {
throw new Error();
}
} catch (error) {
// Failure of this assertion likely means that we've implemented
// `jr:count`. When that occurs, these error condition assertions should
// be removed, and the test should be updated to add expected node-set
// reference assertions in the `scenario.next` calls, and so on, either
// until the test passes or a new known point of failure is identified.

scenario.answer('multi');
scenario.next('/data/not_single');
scenario.next('/data/not_single/gps');
scenario.answer('1.234 5.678');
scenario.next('/data/building_name');
scenario.answer('Some building');
scenario.next('/data/full_address1');
scenario.answer('Some address, some location');
expect(error).toBeInstanceOf(Error);

// endregion
const { message } = error as Error;

expect.fail(
`Update \`child-vaccination.test.ts\`, known failure mode has changed: ${message}`
);
}

// region Answer all household repeats

Expand Down

0 comments on commit 66aa8c9

Please sign in to comment.