From 683ec3300490714da21ef0a1e1e50d348473d868 Mon Sep 17 00:00:00 2001 From: "jeremy@jermolene.com" Date: Mon, 17 Apr 2023 11:13:35 +0100 Subject: [PATCH] Refactor compound tiddler handling into data widget And add some tests for the data widget --- core/modules/widgets/data.js | 87 +++++++++++++++---- core/modules/widgets/testcase.js | 29 +------ .../tests/data/data-widget/ImportCompound.tid | 25 ++++++ .../tests/data/data-widget/ImportFilter.tid | 26 ++++++ .../tests/data/data-widget/ImportTiddler.tid | 21 +++++ .../tests/data/data-widget/Simple.tid | 16 ++++ .../tiddlers/hellothere/HelloThere.tid | 1 + .../tw5.com/tiddlers/widgets/DataWidget.tid | 40 +++++++-- .../tiddlers/widgets/TestCaseWidget.tid | 37 ++------ .../geospatial/docs/geodistance.tid | 3 +- .../tiddlywiki/geospatial/docs/geolookup.tid | 3 +- .../geospatial/docs/geonearestpoint.tid | 3 +- .../tiddlywiki/geospatial/docs/geopoint.tid | 3 +- .../tiddlywiki/geospatial/docs/olc-decode.tid | 3 +- .../tiddlywiki/geospatial/docs/olc-encode.tid | 3 +- 15 files changed, 210 insertions(+), 90 deletions(-) create mode 100644 editions/test/tiddlers/tests/data/data-widget/ImportCompound.tid create mode 100644 editions/test/tiddlers/tests/data/data-widget/ImportFilter.tid create mode 100644 editions/test/tiddlers/tests/data/data-widget/ImportTiddler.tid create mode 100644 editions/test/tiddlers/tests/data/data-widget/Simple.tid diff --git a/core/modules/widgets/data.js b/core/modules/widgets/data.js index c5c2b9e371..4dd14d6166 100644 --- a/core/modules/widgets/data.js +++ b/core/modules/widgets/data.js @@ -31,7 +31,12 @@ DataWidget.prototype.render = function(parent,nextSibling) { this.parentDomNode = parent; this.computeAttributes(); this.execute(); + var domNode = this.document.createTextNode(""); + parent.insertBefore(domNode,nextSibling); this.renderChildren(parent,nextSibling); + // Children must have been rendered before we can read the data values + domNode.textContent = JSON.stringify(this.readDataTiddlerValues()); + this.domNodes.push(domNode); }; /* @@ -52,29 +57,75 @@ DataWidget.prototype.readDataTiddlerValues = function() { // Read any attributes not prefixed with $ $tw.utils.each(this.attributes,function(value,name) { if(name.charAt(0) !== "$") { - item[name] = value; + item[name] = value; } }); - // Deal with $tiddler or $filter attributes - var titles; + item = new $tw.Tiddler(item); + // Deal with $tiddler, $filter or $compound-tiddler attributes + var tiddlers = [],title; if(this.hasAttribute("$tiddler")) { - titles = [this.getAttribute("$tiddler")]; - } else if(this.hasAttribute("$filter")) { - titles = this.wiki.filterTiddlers(this.getAttribute("$filter")); + title = this.getAttribute("$tiddler"); + if(title) { + tiddlers.push(this.wiki.getTiddler(title)); + } } - if(titles) { - var results = []; - $tw.utils.each(titles,function(title,index) { - var tiddler = self.wiki.getTiddler(title), - fields; - if(tiddler) { - fields = tiddler.getFieldStrings(); - } - results.push($tw.utils.extend({},fields,item)); - }) - return results; + if(this.hasAttribute("$filter")) { + var filter = this.getAttribute("$filter"); + if(filter) { + var titles = this.wiki.filterTiddlers(filter); + $tw.utils.each(titles,function(title) { + var tiddler = self.wiki.getTiddler(title); + tiddlers.push(tiddler); + }); + } + } + if(this.hasAttribute("$compound-tiddler")) { + title = this.getAttribute("$compound-tiddler"); + if(title) { + tiddlers.push.apply(tiddlers,this.extractCompoundTiddler(title)); + } + } + // Convert the literal item to field strings + item = item.getFieldStrings(); + if(tiddlers.length === 0) { + if(Object.keys(item).length > 0 && !!item.title) { + return [item]; + } else { + return []; + } } else { - return [item]; + var results = []; + $tw.utils.each(tiddlers,function(tiddler,index) { + var fields = tiddler.getFieldStrings(); + results.push($tw.utils.extend({},fields,item)); + }); + return results; + } +}; + +/* +Helper to extract tiddlers from text/vnd.tiddlywiki-multiple tiddlers +*/ +DataWidget.prototype.extractCompoundTiddler = function(title) { + var tiddler = this.wiki.getTiddler(title); + if(tiddler && tiddler.fields.type === "text/vnd.tiddlywiki-multiple") { + var text = tiddler.fields.text || "", + rawTiddlers = text.split("\n+\n"), + tiddlers = []; + $tw.utils.each(rawTiddlers,function(rawTiddler) { + var fields = Object.create(null), + split = rawTiddler.split(/\r?\n\r?\n/mg); + if(split.length >= 1) { + fields = $tw.utils.parseFields(split[0],fields); + } + if(split.length >= 2) { + fields.text = split.slice(1).join("\n\n"); + } + tiddlers.push(new $tw.Tiddler(fields)); + }); + return tiddlers; + } else { + return []; } }; diff --git a/core/modules/widgets/testcase.js b/core/modules/widgets/testcase.js index be18a39ae3..ab052a59dd 100644 --- a/core/modules/widgets/testcase.js +++ b/core/modules/widgets/testcase.js @@ -53,20 +53,12 @@ TestCaseWidget.prototype.render = function(parent,nextSibling) { Array.prototype.push.apply(tiddlers,widget.readDataTiddlerValues()); }); this.testcaseWiki.addTiddlers(tiddlers); - // Load tiddlers from the optional testcaseTiddler - if(this.testcaseTiddler) { - var tiddler = this.wiki.getTiddler(this.testcaseTiddler); - if(tiddler && tiddler.fields.type === "text/vnd.tiddlywiki-multiple") { - var tiddlers = this.extractMultipleTiddlers(tiddler.fields.text); - this.testcaseWiki.addTiddlers(tiddlers); - } - } // Unpack plugin tiddlers this.testcaseWiki.readPluginInfo(); this.testcaseWiki.registerPluginTiddlers("plugin"); this.testcaseWiki.unpackPluginTiddlers(); this.testcaseWiki.addIndexersToWiki(); - // Gemerate a `transclusion` variable that depends on the values of the payload tiddlers so that the template can easily make unique state tiddlers + // Generate a `transclusion` variable that depends on the values of the payload tiddlers so that the template can easily make unique state tiddlers this.setVariable("transclusion",$tw.utils.hashString(this.testcaseWiki.getTiddlersAsJson("[all[tiddlers]]"))); // Generate a `testcaseInfo` variable that contains information about the subwiki in JSON format var testcaseInfoData = { @@ -80,24 +72,6 @@ TestCaseWidget.prototype.render = function(parent,nextSibling) { this.renderChildren(parent,nextSibling); }; -TestCaseWidget.prototype.extractMultipleTiddlers = function(text) { - // Duplicates code found in $:/plugins/tiddlywiki/jasmine/run-wiki-based-tests.js - var rawTiddlers = text.split("\n+\n"), - tiddlers = []; - $tw.utils.each(rawTiddlers,function(rawTiddler) { - var fields = Object.create(null), - split = rawTiddler.split(/\r?\n\r?\n/mg); - if(split.length >= 1) { - fields = $tw.utils.parseFields(split[0],fields); - } - if(split.length >= 2) { - fields.text = split.slice(1).join("\n\n"); - } - tiddlers.push(fields); - }); - return tiddlers; -}; - /* Render a test case */ @@ -126,7 +100,6 @@ TestCaseWidget.prototype.testcaseRawTiddler = function(parent,nextSibling,title, Compute the internal state of the widget */ TestCaseWidget.prototype.execute = function() { - this.testcaseTiddler = this.getAttribute("testcase-tiddler"); this.testcaseTemplate = this.getAttribute("template","$:/core/ui/testcases/DefaultTemplate"); // Make child widgets var parseTreeNodes = [{ diff --git a/editions/test/tiddlers/tests/data/data-widget/ImportCompound.tid b/editions/test/tiddlers/tests/data/data-widget/ImportCompound.tid new file mode 100644 index 0000000000..6fb642bf70 --- /dev/null +++ b/editions/test/tiddlers/tests/data/data-widget/ImportCompound.tid @@ -0,0 +1,25 @@ +title: Data/ImportCompound +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Importing a compound payload tiddler and adding custom fields + +title: Description +text: Importing a compound payload tiddler and adding custom fields ++ +title: Output + +\whitespace trim +<$data $compound-tiddler="Compound" custom="Alpha"/> ++ +title: Compound +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Payload Tiddler +tags: Alpha Beta Gamma + +This is a payload tiddler from a compound tiddler ++ +title: ExpectedResult + +

[{"title":"Payload Tiddler","tags":"Alpha Beta Gamma","text":"This is a payload tiddler from a compound tiddler","custom":"Alpha"}]

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/data-widget/ImportFilter.tid b/editions/test/tiddlers/tests/data/data-widget/ImportFilter.tid new file mode 100644 index 0000000000..472532ca89 --- /dev/null +++ b/editions/test/tiddlers/tests/data/data-widget/ImportFilter.tid @@ -0,0 +1,26 @@ +title: Data/ImportFilter +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Importing a payload filter and adding custom fields + +title: Description +text: Importing a payload filter and adding custom fields ++ +title: Output + +\whitespace trim +<$data $filter="[tag[Definitions]]" custom="Alpha"/> ++ +title: HelloThere +tags: Definitions + +This is the tiddler HelloThere ++ +title: AnotherDefinition +tags: Definitions + +This is the tiddler AnotherDefinition ++ +title: ExpectedResult + +

[{"title":"AnotherDefinition","tags":"Definitions","text":"This is the tiddler AnotherDefinition","custom":"Alpha"},{"title":"HelloThere","tags":"Definitions","text":"This is the tiddler HelloThere","custom":"Alpha"}]

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/data-widget/ImportTiddler.tid b/editions/test/tiddlers/tests/data/data-widget/ImportTiddler.tid new file mode 100644 index 0000000000..0788fa590f --- /dev/null +++ b/editions/test/tiddlers/tests/data/data-widget/ImportTiddler.tid @@ -0,0 +1,21 @@ +title: Data/ImportTiddler +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Importing a payload tiddler and adding custom fields + +title: Description +text: Importing a payload tiddler and adding custom fields ++ +title: Output + +\whitespace trim +<$data $tiddler="HelloThere" custom="Alpha"/> ++ +title: HelloThere +tags: Definitions + +This is the tiddler HelloThere ++ +title: ExpectedResult + +

[{"title":"HelloThere","tags":"Definitions","text":"This is the tiddler HelloThere","custom":"Alpha"}]

\ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/data-widget/Simple.tid b/editions/test/tiddlers/tests/data/data-widget/Simple.tid new file mode 100644 index 0000000000..81ac2d8cf8 --- /dev/null +++ b/editions/test/tiddlers/tests/data/data-widget/Simple.tid @@ -0,0 +1,16 @@ +title: Data/Simple +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Standalone data widget to create individual tiddlers + +title: Description +text: Standalone data widget to create individual tiddlers ++ +title: Output + +\whitespace trim +<$data title="Epsilon" text="Theta"/> ++ +title: ExpectedResult + +

[{"title":"Epsilon","text":"Theta"}]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid index 4b03f9cc1c..41c99d59c9 100644 --- a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid +++ b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid @@ -16,6 +16,7 @@ A demo of the geospatial functionality is [ext[available here|./plugins/tiddlywi It also includes a number of new features that are intended for eventual inclusion in the core: * TestCaseWidget, TestCaseTranscludeWidget and TestCaseViewWidget +* Enhancements to the existing DataWidget * [[jsonset Operator]] * [[setquerystring Operator]] * [[WidgetMessage: tm-http-request]] diff --git a/editions/tw5.com/tiddlers/widgets/DataWidget.tid b/editions/tw5.com/tiddlers/widgets/DataWidget.tid index 5c4c63ad45..143d462d45 100644 --- a/editions/tw5.com/tiddlers/widgets/DataWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/DataWidget.tid @@ -14,23 +14,22 @@ The data widget is used with the <<.wlink TestCaseWidget>> widget and the [[Inne The content of the data widget is rendered as if the data widget were not present. It supports the following attributes: |!Attribute |!Description | -|<<.attr $tiddler>> |The title of a tiddler to be used as a payload tiddler (optional) | -|<<.attr $filter>> |A filter string identifying tiddlers to be used as payload tiddlers (optional) | +|<<.attr $tiddler>> |Optional title of a tiddler to be used as a payload tiddler (optional) | +|<<.attr $filter>> |Optional filter string identifying tiddlers to be used as payload tiddlers (optional) | +|<<.attr $compound-tiddler>> |Optional title of a tiddler containing payload tiddlers in `text/vnd.tiddlywiki-multiple` format (see below) | |//any attribute
not starting
with $// |Field values to be assigned to the payload tiddler(s) | -The data widget can be used in three different ways: +The data widget is not rendered when used within the <<.wlink TestCaseWidget>> widget or the [[Innerwiki Plugin]] but for ease of testing, when used elsewhere it renders a JSON representation of the payload tiddlers. -* Without the `$tiddler` or `$filter` attributes, the remaining attributes provide the fields for a single payload tiddler -* With the `$tiddler` attribute present, the payload tiddler takes its fields from that tiddler with the remaining attributes overriding those fields -* With the `$filter` attribute present, the payload is a copy of all of the tiddlers identified by the filter, with the remaining attributes overriding those fields of each one - -This example injects a tiddler with the title "Epsilon" and the text "Theta": +Without any of the attributes <<.attr $tiddler>>, <<.attr $filter>> or <<.attr $compound-tiddler>>, any attributes whose name does not start with $ are used as the field values for creating a single new tiddler. For example, here a tiddler with the title "Epsilon" and the text "Theta" is created: ``` <$data title="Epsilon" text="Theta"/> ``` -This example injects a copy of the "HelloThere" tiddler with the addition of the field "custom" set to "Alpha": +If any of the attributes <<.attr $tiddler>>, <<.attr $filter>> or <<.attr $compound-tiddler>> are specified then they are used to generate base tiddlers that are then modified with the addition of fields derived from any attributes whose name does not start with $. + +This example, here we specify a copy of the "HelloThere" tiddler with the addition of the field "custom" set to "Alpha": ``` <$data $tiddler="HelloThere" custom="Alpha"/> @@ -41,3 +40,26 @@ This example injects all image tiddlers with the addition of the field "custom" ``` <$data $filter="[is[image]]" custom="Beta"/> ``` + +! Compound Tiddlers + +Compound tiddlers provide a way to easily create multiple tiddlers from within a single tiddler. They are contained in tiddlers of type `text/vnd.tiddlywiki-multiple`. The text field consists of a series of tiddlers in the same format as `.tid` files, each separated by a line containing a single `+` character. + +For example: + +``` +title: First +tags: one two + +This is the first tiddler ++ +title: Second +tags: three four + +This is the second tiddler ++ +title: third +tags: five six + +This is the third tiddler +``` \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid b/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid index b60c070c2c..3fd3ea939e 100644 --- a/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid @@ -14,38 +14,15 @@ The testcase widget creates an independent subwiki loaded with specified tiddler The content of the `<$testcase>` widget is not displayed but instead is scanned for <<.wlink DataWidget>> widgets that define the payload tiddlers to be included in the test case. |!Attribute |!Description | -|<<.attr testcase-tiddler>> |Optional title of a tiddler containing a test case in `text/vnd.tiddlywiki-multiple` format (see below) | |<<.attr template>> |Optional title of the template used to display the testcase (defaults to $:/core/ui/testcases/DefaultTemplate) | +! State Handling + +The `<$testcase>` widget sets the variable `transclusion` to a hash that reflects the names and values of all the payload tiddlers. This makes easier for test case templates to create unique state tiddler titles using the [[qualify Macro]] or QualifyWidget. + ! Payload Tiddlers -The payload tiddlers are the tiddler values that are loaded into the subwiki that is created to run the tests. - -The payload tiddlers can be created in two different ways: - -* Tiddlers values specfied via <<.wlink DataWidget>> widgets within the body of the `<$testcase>` widget -* As multiple tiddlers embedded within a single tiddler of type `text/vnd.tiddlywiki-multiple` whose title is given in the <<.attr testcase-tiddler>> attribute - -The special tiddler type `text/vnd.tiddlywiki-multiple` is intended to simplify creating test cases that require several tiddlers. The text field consists of a series of tiddlers in the same format as `.tid` files, each separated by a line containing a single `+` character. - -For example: - -``` -title: First -tags: one two - -This is the first tiddler -+ -title: Second -tags: three four - -This is the second tiddler -+ -title: third -tags: five six - -This is the third tiddler -``` +The payload tiddlers are the tiddler values that are loaded into the subwiki that is created to run the tests. They are created via <<.wlink DataWidget>> widgets within the body of the `<$testcase>` widget. ! Test Case Templates @@ -55,9 +32,11 @@ The default test case template $:/core/ui/testcases/DefaultTemplate displays a s The `<$testcase>` widget assigns a value to the `transclusion` variable that depends on the combined values of all the payload tiddlers. This means that using the `<>` macro within a test case template will generate a unique value for each of multiple test cases within the same tiddler. +! Test Case Variables + The `<$testcase>` widget also assigns a block of information about the subwiki in JSON format to a the variable `testcaseInfo`. It contains the following properties: -* ''titles'': an array of the titles of all the tiddlers in the subwiki (excluding shadow tiddlers) +* ''tiddlers'': a hashmap by title of an array of all the field names of each tiddler in the subwiki (does not include shadow tiddlers unless they have been overridden) ! Test Case Conventions diff --git a/plugins/tiddlywiki/geospatial/docs/geodistance.tid b/plugins/tiddlywiki/geospatial/docs/geodistance.tid index 6e62e78a74..b341fef56e 100644 --- a/plugins/tiddlywiki/geospatial/docs/geodistance.tid +++ b/plugins/tiddlywiki/geospatial/docs/geodistance.tid @@ -8,6 +8,7 @@ The `geodistance` operator calculates the distance between two points in [[GeoJS !! Examples -<$testcase testcase-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geodistance"> +<$testcase> +<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geodistance"/> <$data $tiddler="$:/plugins/tiddlywiki/geospatial"/> \ No newline at end of file diff --git a/plugins/tiddlywiki/geospatial/docs/geolookup.tid b/plugins/tiddlywiki/geospatial/docs/geolookup.tid index 31a17ba3c0..3e95139461 100644 --- a/plugins/tiddlywiki/geospatial/docs/geolookup.tid +++ b/plugins/tiddlywiki/geospatial/docs/geolookup.tid @@ -10,6 +10,7 @@ Each input list item is interpreted as a [[GeoJSON Point Feature]] and the opera !! Examples -<$testcase testcase-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geolookup"> +<$testcase> +<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geolookup"/> <$data $tiddler="$:/plugins/tiddlywiki/geospatial"/> \ No newline at end of file diff --git a/plugins/tiddlywiki/geospatial/docs/geonearestpoint.tid b/plugins/tiddlywiki/geospatial/docs/geonearestpoint.tid index 6a44f2b2fe..41620f7054 100644 --- a/plugins/tiddlywiki/geospatial/docs/geonearestpoint.tid +++ b/plugins/tiddlywiki/geospatial/docs/geonearestpoint.tid @@ -8,6 +8,7 @@ The `geonearestpoint` operator determines the point in a list that is nearest to !! Examples -<$testcase testcase-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geonearestpoint"> +<$testcase> +<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geonearestpoint"/> <$data $tiddler="$:/plugins/tiddlywiki/geospatial"/> \ No newline at end of file diff --git a/plugins/tiddlywiki/geospatial/docs/geopoint.tid b/plugins/tiddlywiki/geospatial/docs/geopoint.tid index 637fb3cc0a..4b9edb8fdd 100644 --- a/plugins/tiddlywiki/geospatial/docs/geopoint.tid +++ b/plugins/tiddlywiki/geospatial/docs/geopoint.tid @@ -16,6 +16,7 @@ Any operands that cannot be interpreted as a valid number will be interpreted as !! Examples -<$testcase testcase-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geopoint"> +<$testcase> +<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/geopoint"/> <$data $tiddler="$:/plugins/tiddlywiki/geospatial"/> diff --git a/plugins/tiddlywiki/geospatial/docs/olc-decode.tid b/plugins/tiddlywiki/geospatial/docs/olc-decode.tid index baa4b6478e..e4b3f4f6ca 100644 --- a/plugins/tiddlywiki/geospatial/docs/olc-decode.tid +++ b/plugins/tiddlywiki/geospatial/docs/olc-decode.tid @@ -8,6 +8,7 @@ The `olc-decode` operator converts an [[OpenLocationCode|https://github.com/goog !! Examples -<$testcase testcase-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/olc-decode"> +<$testcase> +<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/olc-decode"/> <$data $tiddler="$:/plugins/tiddlywiki/geospatial"/> \ No newline at end of file diff --git a/plugins/tiddlywiki/geospatial/docs/olc-encode.tid b/plugins/tiddlywiki/geospatial/docs/olc-encode.tid index 6697dd92c9..78443ace54 100644 --- a/plugins/tiddlywiki/geospatial/docs/olc-encode.tid +++ b/plugins/tiddlywiki/geospatial/docs/olc-encode.tid @@ -8,6 +8,7 @@ The `old-encode` operator converts separate latitude and longitude numbers into !! Examples -<$testcase testcase-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/olc-encode"> +<$testcase> +<$data $compound-tiddler="$:/plugins/tiddlywiki/geospatial/tests/operators/olc-encode"/> <$data $tiddler="$:/plugins/tiddlywiki/geospatial"/> \ No newline at end of file