Skip to content

Commit

Permalink
Porting FormDefSerializationTest.java - mostly skipped + context
Browse files Browse the repository at this point in the history
  • Loading branch information
eyelidlessness committed May 16, 2024
1 parent d36a3df commit 1eb6699
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 1 deletion.
7 changes: 7 additions & 0 deletions packages/scenario/src/error/UnclearApplicabilityError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class UnclearApplicabilityError extends Error {
constructor(subject: string) {
super(
`It is not yet clear if/how ${subject} functionality will be applicable in the web forms project`
);
}
}
26 changes: 25 additions & 1 deletion packages/scenario/src/jr/Scenario.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { XFormsElement } from '@odk-web-forms/common/test/fixtures/xform-dsl/XFormsElement.ts';
import type { RootNode } from '@odk-web-forms/xforms-engine';
import type { AnyNode, RootNode } from '@odk-web-forms/xforms-engine';
import type { Accessor, Setter } from 'solid-js';
import { createMemo, createSignal, runWithOwner } from 'solid-js';
import { afterEach, expect } from 'vitest';
Expand All @@ -8,6 +8,7 @@ import { answerOf } from '../client/answerOf.ts';
import type { TestFormResource } from '../client/init.ts';
import { initializeTestForm } from '../client/init.ts';
import { getClosestRepeatRange } from '../client/traversal.ts';
import { UnclearApplicabilityError } from '../error/UnclearApplicabilityError.ts';
import { PositionalEvent } from './event/PositionalEvent.ts';
import { RepeatInstanceEvent } from './event/RepeatInstanceEvent.ts';
import {
Expand All @@ -16,6 +17,7 @@ import {
type NonTerminalPositionalEvent,
type PositionalEvents,
} from './event/getPositionalEvents.ts';
import { TreeReference } from './instance/TreeReference.ts';
import type { PathResource } from './resource/PathResource.ts';
import { SelectChoiceList } from './select/SelectChoiceList.ts';

Expand Down Expand Up @@ -291,4 +293,26 @@ export class Scenario {

this.instanceRoot.setLanguage(language);
}

refAtIndex(): TreeReference {
const event = this.getSelectedPositionalEvent();

let treeReferenceNode: AnyNode;

if (event.eventType === 'END_OF_FORM') {
treeReferenceNode = this.instanceRoot;
} else {
treeReferenceNode = event.node;
}

return new TreeReference(treeReferenceNode);
}

/**
* @todo it is not clear if/how we'll use similar logic in web forms. It
* seems most likely to be applicable to offline capabilities.
*/
serializeAndDeserializeForm(): Promise<Scenario> {
return Promise.reject(new UnclearApplicabilityError('serialization/deserialization'));
}
}
21 changes: 21 additions & 0 deletions packages/scenario/src/jr/instance/TreeReference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { AnyNode } from '@odk-web-forms/xforms-engine';

/**
* Wraps a node (as in `@odk-web-forms/xforms-engine` semantic terms) to provide
* interface compatibility with JavaRosa's `TreeReference`, as minimally as
* possible to satisfy ported test logic/assertions.
*/
export class TreeReference {
constructor(readonly node: AnyNode) {}

/**
* @todo Currently the engine doesn't provide a representation of secondary
* instances **at all**. This seems "correct" for the current engine/client
* responsibility boundaries, but that may change if/as we expand the
* client concept into other areas of functionality beyond the primary form-
* filling UI use cases.
*/
getInstanceName(): string | null {
return null;
}
}
14 changes: 14 additions & 0 deletions packages/scenario/src/value/ExpectedNullValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @todo Before bailing on the port of `FormDefSerializationTest.java`, it
* seemed quite likely there should be a `ComparableValue` abstraction below
* `ComparableAnswer`: `null` is almost certainly not an "answer", but we might
* find that there is a more general sense of comparable **and castable**
* assertion values, of which "answer" may be a more specific case.
*/
export class ExpectedNullValue {
constructor() {
throw new Error('TODO: see `ExpectedNullValue` JSDoc');
}
}

export const nullValue = () => null;
76 changes: 76 additions & 0 deletions packages/scenario/test/serialization.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {
bind,
body,
head,
html,
input,
mainInstance,
model,
t,
title,
} from '@odk-web-forms/common/test/fixtures/xform-dsl/index.ts';
import { describe, expect, it } from 'vitest';
import { Scenario } from '../src/jr/Scenario.ts';
import { nullValue } from '../src/value/ExpectedNullValue.ts';

// Ported as of https://github.com/getodk/javarosa/commit/5ae68946c47419b83e7d28290132d846e457eea6
describe('Serialization', () => {
/**
* **PORTING NOTES**
*
* Discussing these tests in Slack, it was determined that they're not
* currently applicable to the web forms project. The first test is ported
* largely because it was already most of the way there by this point,
* although much of the supporting logic for a more faithful port has been
* omitted for now (with some breadcrumbs on thinking behind that supporting
* logic left in for posterity in case it becomes applicable in the future).
*
* We reached the conclusion to defer porting the rest of these tests for now,
* on the basis that the current serialization functionality under test is in
* support of specific performance optimizations; that the tests exercise
* implementation details not directly pertinent to web forms at this time;
* that the performance optimizations in question are themselves not likely to
* be pertinent to web forms, at least at this time.
*
* We also agreed that this note would serve as an explanation of the above,
* as well as an opportunity to briefly mention that we may have other reasons
* to support (de)serialization, and to test that support, likely around
* offline functionality. As such, we may want to revisit these skipped
* JavaRosa tests if they seem valuable for that effort when we get to it.
*/
describe.skip('FormDefSerializationTest.java', () => {
const getSimplestFormScenario = async (): Promise<Scenario> => {
return Scenario.init(
'Simplest',
html(
head(
title('Simplest'),
model(mainInstance(t('data id="simplest"', t('a'))), bind('/data/a').type('string'))
),
body(input('/data/a'))
)
);
};

describe('instance name', () => {
describe('for reference in main instance', () => {
it.skip('is always null', async () => {
const scenario = await getSimplestFormScenario();

scenario.next('/data/a');

expect(scenario.refAtIndex().getInstanceName()).toEqual(nullValue());

const deserialized = await scenario.serializeAndDeserializeForm();

deserialized.next('/data/a');

expect(deserialized.refAtIndex().getInstanceName()).toEqual(nullValue());
});

it.skip('instanceName_forFormDefEvaluationContext_isAlwaysNull');
it.skip('instanceName_forFormDefMainInstance_isAlwaysNull');
});
});
});
});

0 comments on commit 1eb6699

Please sign in to comment.