You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@annotator.apache.org by ge...@apache.org on 2020/04/03 11:53:19 UTC

[incubator-annotator] 04/09: Test fragment-identifier against spec examples

This is an automated email from the ASF dual-hosted git repository.

gerben pushed a commit to branch fragment-tests
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git

commit 531d5e75d0ec61a4bdf095c96b73633c7fda80cd
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Apr 2 21:46:25 2020 +0200

    Test fragment-identifier against spec examples
    
    Derived from earlier work: https://github.com/Treora/selector-state-frags/blob/51fe53df390301f768b6e876a670d12d1249b2b1/test/test.js
---
 packages/fragment-identifier/test/index.js         |  55 +++++++
 .../fragment-identifier/test/spec-examples.json    | 180 +++++++++++++++++++++
 2 files changed, 235 insertions(+)

diff --git a/packages/fragment-identifier/test/index.js b/packages/fragment-identifier/test/index.js
new file mode 100644
index 0000000..761de0d
--- /dev/null
+++ b/packages/fragment-identifier/test/index.js
@@ -0,0 +1,55 @@
+/**
+ * @license
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { parse, stringify } from '../src';
+
+// Test examples from the spec: https://www.w3.org/TR/2017/NOTE-selectors-states-20170223/#json-examples-converted-to-fragment-identifiers
+import specExamplesRaw from './spec-examples.json';
+
+// The JSON file has the full examples; pull out the parts we need.
+const specExamples = Object.fromEntries(Object.entries(specExamplesRaw).map(
+  ([name, { uri, obj: { selector, state } }]) =>
+    [name, { fragId: uri.split('#')[1], selector, state }]
+));
+
+describe('stringify', () => {
+  // Test examples in the spec, ignoring their URI encoding
+  for (const [name, example] of Object.entries(specExamples)) {
+    it(`should properly stringify (disregarding URI-encoding): '${name}'`, () => {
+      const result = stringify(example.selector || example.state);
+      assert.equal(
+        decodeURIComponent(result),
+        decodeURIComponent(example.fragId)
+      );
+    });
+  }
+});
+
+describe('parse', () => {
+  for (const [name, example] of Object.entries(specExamples)) {
+    it(`should properly parse: ${name}`, () => {
+      const expected = (example.selector !== undefined)
+        ? { selector: example.selector }
+        : { state: example.state };
+      const result = parse(example.fragId);
+      assert.deepEqual(result, expected);
+    });
+  }
+});
diff --git a/packages/fragment-identifier/test/spec-examples.json b/packages/fragment-identifier/test/spec-examples.json
new file mode 100644
index 0000000..832e02b
--- /dev/null
+++ b/packages/fragment-identifier/test/spec-examples.json
@@ -0,0 +1,180 @@
+{
+  "Example 2 <=> 16: Fragment selector": {
+    "uri": "http://example.org/video1#selector(type=FragmentSelector,conformsTo=http://www.w3.org/TR/media-frags/,value=t%3D30%2C60)",
+    "obj": {
+      "source": "http://example.org/video1",
+      "selector": {
+        "type": "FragmentSelector",
+        "conformsTo": "http://www.w3.org/TR/media-frags/",
+        "value": "t=30,60"
+      }
+    }
+  },
+
+  "Example 3 <=> 17: CSS selector": {
+    "uri": "http://example.org/page1.html#selector(type=CssSelector,value=%23elemid%20>%20.elemclass%20+%20p)",
+    "obj": {
+      "source": "http://example.org/page1.html",
+      "selector": {
+        "type": "CssSelector",
+        "value": "#elemid > .elemclass + p"
+      }
+    }
+  },
+
+  "Example 4 <=> 18: XPath selector": {
+    "uri": "http://example.org/page1.html#selector(type=XPathSelector,value=/html/body/p[2]/table/tr[2]/td[3]/span)",
+    "obj": {
+      "source": "http://example.org/page1.html",
+      "selector": {
+        "type": "XPathSelector",
+        "value": "/html/body/p[2]/table/tr[2]/td[3]/span"
+      }
+    }
+  },
+
+  "Example 5 <=> 19: TextQuote selector": {
+    "uri": "http://example.org/page1#selector(type=TextQuoteSelector,exact=annotation,prefix=this%20is%20an%20,suffix=%20that%20has%20some)",
+    "obj": {
+      "source": "http://example.org/page1",
+      "selector": {
+        "type": "TextQuoteSelector",
+        "exact": "annotation",
+        "prefix": "this is an ",
+        "suffix": " that has some"
+      }
+    }
+  },
+
+  "Example 6 <=> 20: TextPosition selector": {
+    "uri": "http://example.org/ebook1#selector(type=TextPositionSelector,start=412,end=795)",
+    "obj": {
+      "source": "http://example.org/ebook1",
+      "selector": {
+        "type": "TextPositionSelector",
+        "start": 412,
+        "end": 795
+      }
+    }
+  },
+
+  "Example 7 <=> 21: Data position selector": {
+    "uri": "http://example.org/diskimg1#selector(type=DataPositionSelector,start=4096,end=4104)",
+    "obj": {
+      "source": "http://example.org/diskimg1",
+      "selector": {
+        "type": "DataPositionSelector",
+        "start": 4096,
+        "end": 4104
+      }
+    }
+  },
+
+  "Example 8 <=> 22: SVG selector; external SVG": {
+    "uri": "http://example.org/map1#selector(type=SvgSelector,id=http://example.org/svg1)",
+    "obj": {
+      "source": "http://example.org/map1",
+      "selector": {
+        "type": "SvgSelector",
+        "id": "http://example.org/svg1"
+      }
+    }
+  },
+
+  "Example 9 <=> 23: SVG selector; embedded SVG": {
+    "uri": "http://example.org/map1#selector(type=SvgSelector,value=<svg:svg>%20...%20</svg:svg>)",
+    "obj": {
+      "source": "http://example.org/map1",
+      "selector": {
+        "type": "SvgSelector",
+        "value": "<svg:svg> ... </svg:svg>"
+      }
+    }
+  },
+
+  "Example 10 <=> 24: Range selector": {
+    "uri": "http://example.org/page1.html#selector(type=RangeSelector,startSelector=selector(type=XPathSelector,value=//table[1]/tr[1]/td[2]),endSelector=selector(type=XPathSelector,value=//table[1]/tr[1]/td[4]))",
+    "obj": {
+      "source": "http://example.org/page1.html",
+      "selector": {
+        "type": "RangeSelector",
+        "startSelector": {
+        "type": "XPathSelector",
+        "value": "//table[1]/tr[1]/td[2]"
+        },
+        "endSelector": {
+        "type": "XPathSelector",
+        "value": "//table[1]/tr[1]/td[4]"
+        }
+      }
+    }
+  },
+
+  "Example 11 <=> 25: Selector refinement": {
+    "uri": "http://example.org/page1#selector(type=FragmentSelector,value=para5,refinedBy=selector(type=TextQuoteSelector,exact=Selected%20Text,prefix=text%20before%20the%20,suffix=%20and%20text%20after%20it))",
+    "obj": {
+      "source": "http://example.org/page1",
+      "selector": {
+        "type": "FragmentSelector",
+        "value": "para5",
+        "refinedBy": {
+        "type": "TextQuoteSelector",
+        "exact": "Selected Text",
+        "prefix": "text before the ",
+        "suffix": " and text after it"
+        }
+      }
+    }
+  },
+
+  "Example 13 <=> 26: Time state": {
+    "uri": "http://example.org/page1#state(type=TimeState,cached=http://archive.example.org/copy1,sourceDate=2015-07-20T13:30:00Z)",
+    "obj": {
+      "source": "http://example.org/page1",
+      "state": {
+        "type": "TimeState",
+        "cached": "http://archive.example.org/copy1",
+        "sourceDate": "2015-07-20T13:30:00Z"
+      }
+    }
+  },
+
+  "Example 14 <=> 27: HTTP request state": {
+    "uri": "http://example.org/resource1#state(type=HttpRequestState,value=Accept:%20application/pdf)",
+    "obj": {
+      "source": "http://example.org/resource1",
+      "state": {
+        "type": "HttpRequestState",
+        "value": "Accept: application/pdf"
+      }
+    }
+  },
+
+  "Example 15 <=> 28: Refinement of states": {
+    "uri": "http://example.org/ebook1#state(type=TimeState,sourceDate=2016-02-01T12:05:23Z,refinedBy=state(type=HttpRequestState,value=Accept:%20application/epub+zip))",
+    "obj": {
+      "source": "http://example.org/ebook1",
+      "state": {
+        "type": "TimeState",
+        "sourceDate": "2016-02-01T12:05:23Z",
+        "refinedBy": {
+        "type": "HttpRequestState",
+        "value": "Accept: application/epub+zip"
+        }
+      }
+    }
+  },
+
+  "Example 29: Serializing IRI to URL (and vice versa)": {
+    "uri": "http://jp.example.org/page1#selector(type=TextQuoteSelector,exact=%E3%83%9A%E3%83%B3%E3%82%92,prefix=%E7%A7%81%E3%81%AF%E3%80%81,suffix=%E6%8C%81%E3%81%A3%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99)",
+    "obj": {
+      "source": "http://jp.example.org/page1",
+      "selector": {
+        "type": "TextQuoteSelector",
+        "exact": "ペンを",
+        "prefix": "私は、",
+        "suffix": "持っています"
+      }
+    }
+  }
+}