Repository: FontoXML/fontoxpath
Pull request #537 (2022-09-12 21:18:38)

Add matching specs for fn:format-integer
Handled according to specs:
- If $value is an empty sequence, the function returns an empty string.
- If the value of $value is negative, the functions returns  the absolute value of $value, and a minus sign is prepended to the result

The spec doesn’t prescribe anything for 0, however the function will always return "-"


bundlemon[bot]: <!-- bundlemon -->
## BundleMon

  
  
  
  
  
<details open>
<summary>Files updated (2)</summary>

Status | Path | Size | Limits
:------------: | ------------ | :------------: | :------------:
:white_check_mark: | fontoxpath.js<br/> | 82.25KB (+109B +0.13%) | -
:white_check_mark: | fontoxpath.esm.js<br/> | 82.36KB (+106B +0.13%) | -
</details>

  
  


Total files change +215B +0.13%

Final result: :white_check_mark:

[View report in BundleMon website ➡️](https://app.bundlemon.dev/projects/609024effb87a20009a6b096/reports/631fa2a51258d9f62e2f2a92)

---
<p align="center"><a href="https://app.bundlemon.dev/projects/609024effb87a20009a6b096/reports?branch=format-integer-specs&resolution=all" target="_blank" rel="noreferrer noopener">Current branch size history</a> | <a href="https://app.bundlemon.dev/projects/609024effb87a20009a6b096/reports?branch=master&resolution=days" target="_blank" rel="noreferrer noopener">Target branch size history</a></p>
coveralls: 
[![Coverage Status](https://coveralls.io/builds/52417433/badge)](https://coveralls.io/builds/52417433)

Coverage increased (+0.02%) to 91.191% when pulling **b49cd490ae3bcd5da7d8052e3f82f83aca52d14b on format-integer-specs** into **91018fe677c8e90e14325bdb0d48b9be76e0ed64 on master**.


Patch:
diff --git a/src/expressions/axes/DescendantAxis.ts b/src/expressions/axes/DescendantAxis.ts
index 422861ac..7c98af7a 100644
--- a/src/expressions/axes/DescendantAxis.ts
+++ b/src/expressions/axes/DescendantAxis.ts
@@ -76,9 +76,11 @@ class DescendantAxis extends Expression {
 		// instructions). Specifying this allows an external system to minimize the dependencies of
 		// an expression with the form 'descendant:ele' to only be invalidated by element
 		// insertions/deletions, not by textnode insertions/deletions.
+		// Similarly, `type-1-or-type-2` can only include elements: attributes are not reachable with the descendant axis
 		const testBucket = this._descendantExpression.getBucket();
 		const onlyElementDescendants =
-			testBucket && (testBucket.startsWith('name-') || testBucket === 'type-1');
+			(testBucket && (testBucket.startsWith('name-') || testBucket === 'type-1')) ||
+			testBucket === 'type-1-or-type-2';
 		this._descendantBucket = onlyElementDescendants ? 'type-1' : null;
 	}
 
diff --git a/src/expressions/functions/builtInFunctions_numeric.ts b/src/expressions/functions/builtInFunctions_numeric.ts
index 2a397052..0db27353 100644
--- a/src/expressions/functions/builtInFunctions_numeric.ts
+++ b/src/expressions/functions/builtInFunctions_numeric.ts
@@ -49,6 +49,15 @@ const ROMAN_NUMBERS = [
 
 function convertIntegerToRoman(integer: string, isLowerCase?: boolean) {
 	let int = parseInt(integer, 10);
+
+	const isNegative = int < 0;
+
+	int = Math.abs(int);
+
+	if (!int) {
+		return '-';
+	}
+
 	let romanString = ROMAN_NUMBERS.reduce((str, roman) => {
 		const q = Math.floor(int / roman.decimal);
 		int -= q * roman.decimal;
@@ -59,6 +68,10 @@ function convertIntegerToRoman(integer: string, isLowerCase?: boolean) {
 		romanString = romanString.toLowerCase();
 	}
 
+	if (isNegative) {
+		romanString = `-${romanString}`;
+	}
+
 	return romanString;
 }
 
@@ -67,9 +80,14 @@ const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
 function convertIntegerToAlphabet(integer: string, isLowerCase?: boolean) {
 	let int = parseInt(integer, 10);
 
+	const isNegative = int < 0;
+
+	int = Math.abs(int);
+
 	if (!int) {
 		return '-';
 	}
+
 	let output = '';
 	let digit;
 
@@ -83,6 +101,10 @@ function convertIntegerToAlphabet(integer: string, isLowerCase?: boolean) {
 		output = output.toLowerCase();
 	}
 
+	if (isNegative) {
+		output = `-${output}`;
+	}
+
 	return output;
 }
 
@@ -106,6 +128,11 @@ const fnFormatInteger: FunctionDefinitionType = (
 ) => {
 	const sequenceValue = sequence.first();
 	const pictureValue = pictureSequence.first();
+
+	if (sequence.isEmpty()) {
+		return sequenceFactory.singleton(createAtomicValue('', ValueType.XSSTRING));
+	}
+
 	switch (pictureValue.value) {
 		case 'I': {
 			const romanString = convertIntegerToRoman(sequenceValue.value);


Test patch:
diff --git a/src/expressions/tests/NameTest.ts b/src/expressions/tests/NameTest.ts
index 061ae692..a1ef2da9 100644
--- a/src/expressions/tests/NameTest.ts
+++ b/src/expressions/tests/NameTest.ts
@@ -10,10 +10,10 @@ import { Bucket } from '../util/Bucket';
 import TestAbstractExpression from './TestAbstractExpression';
 
 class NameTest extends TestAbstractExpression {
-	public _kind: number;
-	public _localName: string;
-	public _namespaceURI: string;
-	public _prefix: string;
+	private _kind: number;
+	private _localName: string;
+	private _namespaceURI: string;
+	private _prefix: string;
 
 	constructor(
 		name: { localName: string; namespaceURI: string | null; prefix: string },
diff --git a/test/assets/unrunnableTestCases.csv b/test/assets/unrunnableTestCases.csv
index b7112918..bdcc54e8 100644
--- a/test/assets/unrunnableTestCases.csv
+++ b/test/assets/unrunnableTestCases.csv
@@ -513,7 +513,6 @@ format-integer-032-it,Error: XPST0017: Function Q{http://www.w3.org/2005/xpath-f
 format-integer-033,Error: Picture: 1;o(-en) is not implemented yet. The supported picture strings are "A", "a", "I", and "i"
 format-integer-034,AssertionError: expected [Function] to throw error matching /FODF1310/ but got 'Picture: 1;o(-er)z is not implemented…'
 format-integer-035,Error: Picture: Ww;t is not implemented yet. The supported picture strings are "A", "a", "I", and "i"
-format-integer-036,Error: Picture: Ww is not implemented yet. The supported picture strings are "A", "a", "I", and "i"
 format-integer-037,AssertionError: expected [Function] to throw error matching /FODF1310/ but got 'Picture: Ww;o()( is not implemented y…'
 format-integer-038,Error: Picture: ()Ww;o is not implemented yet. The supported picture strings are "A", "a", "I", and "i"
 format-integer-039,Error: Picture:   is not implemented yet. The supported picture strings are "A", "a", "I", and "i"
diff --git a/test/specs/parsing/axes/DescendantAxis.tests.ts b/test/specs/parsing/axes/DescendantAxis.tests.ts
index ce2b585d..08bd6659 100644
--- a/test/specs/parsing/axes/DescendantAxis.tests.ts
+++ b/test/specs/parsing/axes/DescendantAxis.tests.ts
@@ -25,28 +25,29 @@ describe('descendant', () => {
 
 		const testDomFacade: IDomFacade = {
 			getFirstChild: (node: slimdom.Node, bucket: string | null) => {
-				console.log(node.nodeName);
-				chai.assert.equal(expectedBucket, bucket);
+				chai.assert.equal(bucket, expectedBucket);
 				return node.firstChild;
 			},
 			getChildNodes: (node: slimdom.Node, bucket: string | null) => {
-				console.log(node.nodeName);
-				chai.assert.equal(expectedBucket, bucket);
+				chai.assert.equal(bucket, expectedBucket);
 				return node.childNodes;
 			},
 			getNextSibling: (node: slimdom.Node, bucket: string | null) => {
-				console.log(node.nodeName);
-				chai.assert.equal(expectedBucket, bucket);
+				chai.assert.equal(bucket, expectedBucket);
 				return node.nextSibling;
 			},
 			getParentNode: (node: slimdom.Node, bucket: string | null) => {
-				console.log(node.nodeName);
-				chai.assert.equal(expectedBucket, bucket);
+				chai.assert.equal(bucket, expectedBucket);
 				return node.parentNode;
 			},
 		} as any;
 
 		evaluateXPathToNodes('descendant::childElement', documentNode.firstChild, testDomFacade);
+
+		// Same for `*`
+		evaluateXPathToNodes('descendant::*', documentNode.firstChild, testDomFacade);
+		// Same for `element()`
+		evaluateXPathToNodes('descendant::element()', documentNode.firstChild, testDomFacade);
 	});
 });
 
diff --git a/test/specs/parsing/functions/functions.numeric.tests.ts b/test/specs/parsing/functions/functions.numeric.tests.ts
index 1bdee601..f0915c31 100644
--- a/test/specs/parsing/functions/functions.numeric.tests.ts
+++ b/test/specs/parsing/functions/functions.numeric.tests.ts
@@ -224,6 +224,10 @@ describe('numeric functions', () => {
 			chai.assert.equal(evaluateXPathToString('format-integer(57, "i")'), 'lvii');
 		});
 
+		it('returns -lvii for -57', () => {
+			chai.assert.equal(evaluateXPathToString('format-integer(-57, "i")'), '-lvii');
+		});
+
 		it('returns G for 7', () => {
 			chai.assert.equal(evaluateXPathToString('format-integer(7, "A")'), 'G');
 		});
@@ -232,6 +236,10 @@ describe('numeric functions', () => {
 			chai.assert.equal(evaluateXPathToString('format-integer(7, "a")'), 'g');
 		});
 
+		it('returns -g for -7', () => {
+			chai.assert.equal(evaluateXPathToString('format-integer(-7, "a")'), '-g');
+		});
+
 		it('returns AB for 28', () => {
 			chai.assert.equal(evaluateXPathToString('format-integer(28, "A")'), 'AB');
 		});
@@ -240,6 +248,24 @@ describe('numeric functions', () => {
 			chai.assert.equal(evaluateXPathToString('format-integer(28, "a")'), 'ab');
 		});
 
+		it('returns -ab for -28', () => {
+			chai.assert.equal(evaluateXPathToString('format-integer(-28, "a")'), '-ab');
+		});
+
+		it('returns - for 0', () => {
+			chai.assert.equal(evaluateXPathToString('format-integer(0, "a")'), '-');
+			chai.assert.equal(evaluateXPathToString('format-integer(0, "A")'), '-');
+			chai.assert.equal(evaluateXPathToString('format-integer(0, "i")'), '-');
+			chai.assert.equal(evaluateXPathToString('format-integer(0, "I")'), '-');
+		});
+
+		it('returns "" for ()', () => {
+			chai.assert.equal(evaluateXPathToString('format-integer((), "a")'), '');
+			chai.assert.equal(evaluateXPathToString('format-integer((), "A")'), '');
+			chai.assert.equal(evaluateXPathToString('format-integer((), "i")'), '');
+			chai.assert.equal(evaluateXPathToString('format-integer((), "I")'), '');
+		});
+
 		it('throws for unknown pictures', () => {
 			chai.assert.throw(() => {
 				evaluateXPathToString('format-integer(57, "x")');