diff --git a/.changeset/yummy-meals-care.md b/.changeset/yummy-meals-care.md new file mode 100644 index 000000000..9b57d66b0 --- /dev/null +++ b/.changeset/yummy-meals-care.md @@ -0,0 +1,5 @@ +--- +'@getodk/xpath': patch +--- + +Stringify NaN as an empty string according to the XForms specs. diff --git a/packages/scenario/test/instance-input.test.ts b/packages/scenario/test/instance-input.test.ts index 260b313f6..dca4aa90b 100644 --- a/packages/scenario/test/instance-input.test.ts +++ b/packages/scenario/test/instance-input.test.ts @@ -812,7 +812,7 @@ describe.each([ readonly inner3: ComparableAnswer; } - const NAN_ANSWER = stringAnswer(String(NaN)); + const NAN_ANSWER = stringAnswer(''); interface MissingRepeatInstanceInputCase { readonly detail: string; diff --git a/packages/xforms-engine/test/instance/instance.test.ts b/packages/xforms-engine/test/instance/instance.test.ts index 717039949..c3f55893b 100644 --- a/packages/xforms-engine/test/instance/instance.test.ts +++ b/packages/xforms-engine/test/instance/instance.test.ts @@ -133,7 +133,7 @@ describe('Form instance state', () => { it.each([ { firstValue: '2', expected: '4' }, - { firstValue: '', expected: 'NaN' }, + { firstValue: '', expected: '' }, ])( 'updates the calculation to $expected when its dependency value is updated to $firstValue', ({ firstValue, expected }) => { @@ -331,7 +331,7 @@ describe('Form instance state', () => { it.each([ { firstValue: '2', expected: '6' }, { firstValue: '0', expected: '0' }, - { firstValue: '', expected: 'NaN' }, + { firstValue: '', expected: '' }, ])( 'updates the calculated value $expected while it is relevant', ({ firstValue, expected }) => { @@ -350,7 +350,7 @@ describe('Form instance state', () => { it.each([ { firstValue: '2', expected: '6' }, { firstValue: '0', expected: '0' }, - { firstValue: '', expected: 'NaN' }, + { firstValue: '', expected: '' }, ])( 'updates the calculated value $expected when it becomes relevant after the calculated dependency has been updated', ({ firstValue, expected }) => { diff --git a/packages/xpath/src/evaluations/NumberEvaluation.ts b/packages/xpath/src/evaluations/NumberEvaluation.ts index ea380951d..bc1ae43a3 100644 --- a/packages/xpath/src/evaluations/NumberEvaluation.ts +++ b/packages/xpath/src/evaluations/NumberEvaluation.ts @@ -18,6 +18,6 @@ export class NumberEvaluation extends ValueEvaluation { testContext.assertStringValue('coalesce(/simple/xpath/to/node, "SECOND")', 'SECOND'); }); + it('should return second value if first value is NaN', () => { + testContext.assertStringValue('coalesce(1 * /simple/xpath/to/node, "0")', '0'); + testContext.assertStringValue( + 'coalesce(/simple/xpath/to/node * /simple/xpath/to/node, "0")', + '0' + ); + }); + it('coalesce(self::*)', () => { testContext = createXFormsTestContext(`
diff --git a/packages/xpath/test/xforms/once.test.ts b/packages/xpath/test/xforms/once.test.ts index 0b730561f..6308fa682 100644 --- a/packages/xpath/test/xforms/once.test.ts +++ b/packages/xpath/test/xforms/once.test.ts @@ -27,8 +27,8 @@ describe('once()', () => { }); }); - it('should set value to NaN', () => { - testContext.assertStringValue('once(. * 10)', 'NaN', { + it('should set value to empty string when arithmetic on empty node produces NaN', () => { + testContext.assertStringValue('once(. * 10)', '', { contextNode, }); }); diff --git a/packages/xpath/test/xforms/randomize.test.ts b/packages/xpath/test/xforms/randomize.test.ts index a45b74877..1fe2a43e3 100644 --- a/packages/xpath/test/xforms/randomize.test.ts +++ b/packages/xpath/test/xforms/randomize.test.ts @@ -80,7 +80,7 @@ describe('randomize()', () => { { seed: 0, expected: 'CBEAFD' }, { seed: NaN, expected: 'CBEAFD' }, { seed: Infinity, expected: 'CBEAFD' }, - { seed: -Infinity, expected: 'CFBEAD' }, + { seed: -Infinity, expected: 'CBEAFD' }, { seed: 'floor(1.1)', expected: 'BFEACD' }, { seed: '//xhtml:div[@id="testFunctionNodeset2"]/xhtml:p', expected: 'BFEACD' }, { seed: MIRROR_HASH_VALUE, expected: MIRROR_HASH_SORT_ORDER },