diff --git a/core/language/en-GB/ControlPanel.multids b/core/language/en-GB/ControlPanel.multids index d8321edbf..93cfc3c10 100644 --- a/core/language/en-GB/ControlPanel.multids +++ b/core/language/en-GB/ControlPanel.multids @@ -206,6 +206,12 @@ Stylesheets/Caption: Stylesheets Stylesheets/Expand/Caption: Expand All Stylesheets/Hint: This is the rendered CSS of the current stylesheet tiddlers tagged with <> Stylesheets/Restore/Caption: Restore +TestCases/Caption: Test Cases +TestCases/Hint: Test cases are self contained examples for testing and learning +TestCases/All/Caption: All Test Cases +TestCases/All/Hint: All Test Cases +TestCases/Failed/Caption: Failed Test Cases +TestCases/Failed/Hint: Only Failed Test Cases Theme/Caption: Theme Theme/Prompt: Current theme: TiddlerFields/Caption: Tiddler Fields diff --git a/core/language/en-GB/Docs/PaletteColours.multids b/core/language/en-GB/Docs/PaletteColours.multids index 98addbf85..1c671a67c 100644 --- a/core/language/en-GB/Docs/PaletteColours.multids +++ b/core/language/en-GB/Docs/PaletteColours.multids @@ -65,6 +65,9 @@ sidebar-tab-foreground-selected: Sidebar tab foreground for selected tabs sidebar-tab-foreground: Sidebar tab foreground sidebar-tiddler-link-foreground-hover: Sidebar tiddler link foreground hover sidebar-tiddler-link-foreground: Sidebar tiddler link foreground +testcase-accent-level-1: Test case accent colour with no nesting +testcase-accent-level-2: Test case accent colour with 2nd level nesting +testcase-accent-level-3: Test case accent colour with 3rd level nesting or higher site-title-foreground: Site title foreground static-alert-foreground: Static alert foreground tab-background-selected: Tab background for selected tabs diff --git a/core/modules/widgets/data.js b/core/modules/widgets/data.js new file mode 100644 index 000000000..c46dd1fb8 --- /dev/null +++ b/core/modules/widgets/data.js @@ -0,0 +1,145 @@ +/*\ +title: $:/core/modules/widgets/data.js +type: application/javascript +module-type: widget + +Widget to dynamically represent one or more tiddlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var DataWidget = function(parseTreeNode,options) { + this.dataWidgetTag = parseTreeNode.type; + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +DataWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +DataWidget.prototype.render = function(parent,nextSibling) { + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + var jsonPayload = JSON.stringify(this.readDataTiddlerValues(),null,4); + var textNode = this.document.createTextNode(jsonPayload); + parent.insertBefore(textNode,nextSibling); + this.domNodes.push(textNode); +}; + +/* +Compute the internal state of the widget +*/ +DataWidget.prototype.execute = function() { + // Construct the child widgets + this.makeChildWidgets(); +}; + +/* +Read the tiddler value(s) from a data widget – must be called after the .render() method +*/ +DataWidget.prototype.readDataTiddlerValues = function() { + var self = this; + // Start with a blank object + var item = Object.create(null); + // Read any attributes not prefixed with $ + $tw.utils.each(this.attributes,function(value,name) { + if(name.charAt(0) !== "$") { + item[name] = value; + } + }); + item = new $tw.Tiddler(item); + // Deal with $tiddler, $filter or $compound-tiddler attributes + var tiddlers = [],title; + if(this.hasAttribute("$tiddler")) { + title = this.getAttribute("$tiddler"); + if(title) { + var tiddler = this.wiki.getTiddler(title); + if(tiddler) { + tiddlers.push(tiddler); + } + } + } + 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 { + 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(/\r?\n\+\r?\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 []; + } +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +DataWidget.prototype.refresh = function(changedTiddlers) { + // It would be expensive to calculate whether the changedTiddlers impact the filter + // identified by the $filter attribute so we just refresh ourselves unconditionally + this.refreshSelf(); + return true; +}; + +exports.data = DataWidget; + +})(); diff --git a/core/modules/widgets/testcase.js b/core/modules/widgets/testcase.js new file mode 100644 index 000000000..abb8f03f6 --- /dev/null +++ b/core/modules/widgets/testcase.js @@ -0,0 +1,160 @@ +/*\ +title: $:/core/modules/widgets/testcase.js +type: application/javascript +module-type: widget + +Widget to display a test case + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var TestCaseWidget = function(parseTreeNode,options) { + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +TestCaseWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +TestCaseWidget.prototype.render = function(parent,nextSibling) { + var self = this; + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + // Create container DOM node + var domNode = this.document.createElement("div"); + this.domNodes.push(domNode); + parent.insertBefore(domNode,nextSibling); + // Render the children into a hidden DOM node + var parser = { + tree: [{ + type: "widget", + attributes: {}, + orderedAttributes: [], + children: this.parseTreeNode.children || [] + }] + }; + this.contentRoot = this.wiki.makeWidget(parser,{ + document: $tw.fakeDocument, + parentWidget: this + }); + this.contentContainer = $tw.fakeDocument.createElement("div"); + this.contentRoot.render(this.contentContainer,null); + // Create a wiki + this.testcaseWiki = new $tw.Wiki(); + // Always load the core plugin + var loadTiddler = function(title) { + var tiddler = self.wiki.getTiddler(title); + if(tiddler) { + self.testcaseWiki.addTiddler(tiddler); + } + } + loadTiddler("$:/core"); + loadTiddler("$:/plugins/tiddlywiki/codemirror"); + // Load tiddlers from child data widgets + var tiddlers = []; + this.findChildrenDataWidgets(this.contentRoot.children,"data",function(widget) { + Array.prototype.push.apply(tiddlers,widget.readDataTiddlerValues()); + }); + var jsonPayload = JSON.stringify(tiddlers); + this.testcaseWiki.addTiddlers(tiddlers); + // Unpack plugin tiddlers + this.testcaseWiki.readPluginInfo(); + this.testcaseWiki.registerPluginTiddlers("plugin"); + this.testcaseWiki.unpackPluginTiddlers(); + this.testcaseWiki.addIndexersToWiki(); + // 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(jsonPayload)); + // Generate a `payloadTiddlers` variable that contains the payload in JSON format + this.setVariable("payloadTiddlers",jsonPayload); + // Render the test rendering if required + if(this.testcaseTestOutput && this.testcaseTestExpectedResult) { + var testcaseOutputContainer = $tw.fakeDocument.createElement("div"); + var testcaseOutputWidget = this.testcaseWiki.makeTranscludeWidget(this.testcaseTestOutput,{ + document: $tw.fakeDocument, + parseAsInline: false, + parentWidget: this, + variables: { + currentTiddler: this.testcaseTestOutput + } + }); + testcaseOutputWidget.render(testcaseOutputContainer); + } + // Clear changes queue + this.testcaseWiki.clearTiddlerEventQueue(); + // Run the actions if provided + if(this.testcaseWiki.tiddlerExists(this.testcaseTestActions)) { + testcaseOutputWidget.invokeActionString(this.testcaseWiki.getTiddlerText(this.testcaseTestActions)); + testcaseOutputWidget.refresh(this.testcaseWiki.changedTiddlers,testcaseOutputContainer); + } + // Set up the test result variables + var testResult = "", + outputHTML = "", + expectedHTML = ""; + if(this.testcaseTestOutput && this.testcaseTestExpectedResult) { + outputHTML = testcaseOutputContainer.children[0].innerHTML; + expectedHTML = this.testcaseWiki.getTiddlerText(this.testcaseTestExpectedResult); + if(outputHTML === expectedHTML) { + testResult = "pass"; + } else { + testResult = "fail"; + } + this.setVariable("outputHTML",outputHTML); + this.setVariable("expectedHTML",expectedHTML); + this.setVariable("testResult",testResult); + this.setVariable("currentTiddler",this.testcaseTestOutput); + } + // Don't display anything if testHideIfPass is "yes" and the tests have passed + if(this.testcaseHideIfPass === "yes" && testResult === "pass") { + return; + } + // Render the page root template of the subwiki + var rootWidget = this.testcaseWiki.makeTranscludeWidget(this.testcaseTemplate,{ + document: this.document, + parseAsInline: false, + parentWidget: this + }); + rootWidget.render(domNode); + // Trap changes in the wiki and refresh the rendering + this.testcaseWiki.addEventListener("change",function(changes) { + rootWidget.refresh(changes,domNode); + }); +}; + +/* +Compute the internal state of the widget +*/ +TestCaseWidget.prototype.execute = function() { + this.testcaseTemplate = this.getAttribute("template","$:/core/ui/testcases/DefaultTemplate"); + this.testcaseTestOutput = this.getAttribute("testOutput"); + this.testcaseTestActions = this.getAttribute("testActions"); + this.testcaseTestExpectedResult = this.getAttribute("testExpectedResult"); + this.testcaseHideIfPass = this.getAttribute("testHideIfPass"); +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +TestCaseWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + if($tw.utils.count(changedAttributes) > 0) { + this.refreshSelf(); + return true; + } else { + return this.contentRoot.refresh(changedTiddlers); + } +}; + +exports["testcase"] = TestCaseWidget; + +})(); diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index ea300ca0e..69f63a684 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -813,6 +813,21 @@ Widget.prototype.allowActionPropagation = function() { return true; }; +/* +Find child <$data> widgets recursively. The tag name allows aliased versions of the widget to be found too +*/ +Widget.prototype.findChildrenDataWidgets = function(children,tag,callback) { + var self = this; + $tw.utils.each(children,function(child) { + if(child.dataWidgetTag === tag) { + callback(child); + } + if(child.children) { + self.findChildrenDataWidgets(child.children,tag,callback); + } + }); +}; + /* Evaluate a variable with parameters. This is a static convenience method that attempts to evaluate a variable as a function, returning an array of strings */ diff --git a/core/palettes/Vanilla.tid b/core/palettes/Vanilla.tid index 4c660e912..c7c800046 100644 --- a/core/palettes/Vanilla.tid +++ b/core/palettes/Vanilla.tid @@ -95,6 +95,9 @@ table-footer-background: #a8a8a8 table-header-background: #f0f0f0 tag-background: #ec6 tag-foreground: #ffffff +testcase-accent-level-1: #84C5E6 +testcase-accent-level-2: #E3B740 +testcase-accent-level-3: #5FD564 tiddler-background: <> tiddler-border: <> tiddler-controls-foreground-hover: #888888 diff --git a/core/ui/ControlPanel/TestCases.tid b/core/ui/ControlPanel/TestCases.tid new file mode 100644 index 000000000..401e14113 --- /dev/null +++ b/core/ui/ControlPanel/TestCases.tid @@ -0,0 +1,10 @@ +title: $:/core/ui/ControlPanel/TestCases +tags: $:/tags/ControlPanel/Advanced +caption: {{$:/language/ControlPanel/TestCases/Caption}} + +\whitespace trim +{{$:/language/ControlPanel/TestCases/Hint}} + +
+<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/TestCases]!has[draft.of]]" default="$:/core/ui/ControlPanel/TestCases/All"/> +
diff --git a/core/ui/ControlPanel/TestCasesAll.tid b/core/ui/ControlPanel/TestCasesAll.tid new file mode 100644 index 000000000..571fb93c2 --- /dev/null +++ b/core/ui/ControlPanel/TestCasesAll.tid @@ -0,0 +1,24 @@ +title: $:/core/ui/ControlPanel/TestCases/All +tags: $:/tags/ControlPanel/TestCases +caption: {{$:/language/ControlPanel/TestCases/All/Caption}} + +\define lingo-base() $:/language/ControlPanel/ +<> + +<$list filter="[all[tiddlers+shadows]tag[$:/tags/wiki-test-spec]type[text/vnd.tiddlywiki-multiple]] [all[tiddlers+shadows]tag[$:/tags/wiki-test-spec-failing]type[text/vnd.tiddlywiki-multiple]]"> + +

+ +<$link> + +<$text text=<>/> + + + +

+ +<$transclude + $tiddler="$:/core/ui/TestCaseTemplate" +/> + + diff --git a/core/ui/ControlPanel/TestCasesFailed.tid b/core/ui/ControlPanel/TestCasesFailed.tid new file mode 100644 index 000000000..4ab2d062d --- /dev/null +++ b/core/ui/ControlPanel/TestCasesFailed.tid @@ -0,0 +1,15 @@ +title: $:/core/ui/ControlPanel/TestCases/Failed +tags: $:/tags/ControlPanel/TestCases +caption: {{$:/language/ControlPanel/TestCases/Failed/Caption}} + +\define lingo-base() $:/language/ControlPanel/ +<> + +<$list filter="[all[tiddlers+shadows]tag[$:/tags/wiki-test-spec]type[text/vnd.tiddlywiki-multiple]] [all[tiddlers+shadows]tag[$:/tags/wiki-test-spec-failing]type[text/vnd.tiddlywiki-multiple]]"> + +<$transclude + $tiddler="$:/core/ui/TestCaseTemplate" + hideIfPass="yes" +/> + + diff --git a/core/ui/TestCaseTemplate.tid b/core/ui/TestCaseTemplate.tid new file mode 100644 index 000000000..74b6ab27d --- /dev/null +++ b/core/ui/TestCaseTemplate.tid @@ -0,0 +1,18 @@ +title: $:/core/ui/TestCaseTemplate + +\parameters (hideIfPass:"no") +\whitespace trim +<$let + linkTarget="yes" + displayFormat={{!!display-format}} +> + <$testcase + testOutput="Output" + testExpectedResult="ExpectedResult" + testActions="Actions" + testHideIfPass=<> + > + <$data $compound-tiddler=<>/> + <$data title="Description" text={{!!description}}/> + + diff --git a/core/ui/TestCases/DefaultTemplate.tid b/core/ui/TestCases/DefaultTemplate.tid new file mode 100644 index 000000000..679620969 --- /dev/null +++ b/core/ui/TestCases/DefaultTemplate.tid @@ -0,0 +1,64 @@ +title: $:/core/ui/testcases/DefaultTemplate + +\whitespace trim +\procedure linkcatcherActions() +<%if [has[title]] %> + <$qualify title=<> name="qualifiedState"> + <$action-setfield $tiddler=<> text=<>/> + +<%endif%> +\end + +<$let + state={{{ [] }}} +> +
+
+

+ <$genesis $type={{{ [!match[]then[$link]else[div]] }}}> + <%if [!match[]] %> + !match[fail]then[tc-test-case-result-icon-pass]] [match[fail]then[tc-test-case-result-icon-fail]] +[join[ ]] }}}> + <%if [!match[fail]] %> + {{$:/core/images/done-button}} + <%else%> + {{$:/core/images/close-button}} + <%endif%> + + <%endif%> + <$view tiddler="Description" mode="inline"/> + +

+
+ <%if [[Narrative]is[tiddler]] %> +
+ <$transclude $tiddler="Narrative" mode="block"/> +
+ <%endif%> + <%if [match[fail]] %> +
+
+ TEST FAILED +
+
+ <$diff-text source=<> dest=<>/> +
+
+ <%endif%> +
+
+ <$macrocall $name="tabs" tabsList="[all[tiddlers]sort[]] -[prefix] -Description -Narrative -ExpectedResult -Output Output +[putfirst[]] -[has[plugin-type]]" state=<> default="Output" template="$:/core/ui/testcases/DefaultTemplate/SourceTabs"/> +
+
+
+
+ <%if [!match[]else[wikitext]match[plaintext]] %> +
<$view tiddler="Output" format="plainwikified" mode="block"/>
+ <%else%> + <$linkcatcher actions=<>> + <$transclude $tiddler="Output" $mode="block"/> + + <%endif%> +
+
+
+ diff --git a/core/ui/TestCases/DefaultTemplateSourceTabs.tid b/core/ui/TestCases/DefaultTemplateSourceTabs.tid new file mode 100644 index 000000000..68c62c1f6 --- /dev/null +++ b/core/ui/TestCases/DefaultTemplateSourceTabs.tid @@ -0,0 +1,24 @@ +title: $:/core/ui/testcases/DefaultTemplate/SourceTabs + +\whitespace trim +\procedure body() +<$list filter="[fields[]] -text +[limit[1]]" variable="ignore"> + + + <$list filter="[fields[]sort[]] -text -title title +[putfirst[]]" variable="fieldName"> + + + + + + +
+ <$text text=<>/> + + <$view tiddler=<> field=<>/> +
+ +<$edit class="tc-edit-texteditor" tiddler=<>/> +\end + +<$transclude $variable="body" $mode="inline"/> diff --git a/core/ui/TestCases/RawJSONTemplate.tid b/core/ui/TestCases/RawJSONTemplate.tid new file mode 100644 index 000000000..fe9c583e8 --- /dev/null +++ b/core/ui/TestCases/RawJSONTemplate.tid @@ -0,0 +1,4 @@ +title: $:/core/ui/testcases/RawJSONTemplate + +\whitespace trim +<$text text=<>/> diff --git a/core/wiki/config/ViewTemplateBodyFilters.multids b/core/wiki/config/ViewTemplateBodyFilters.multids index ff9fe7250..e1dd62880 100644 --- a/core/wiki/config/ViewTemplateBodyFilters.multids +++ b/core/wiki/config/ViewTemplateBodyFilters.multids @@ -1,6 +1,7 @@ title: $:/config/ViewTemplateBodyFilters/ tags: $:/tags/ViewTemplateBodyFilter +testcase: [tag[$:/tags/wiki-test-spec]type[text/vnd.tiddlywiki-multiple]then[$:/core/ui/TestCaseTemplate]] [tag[$:/tags/wiki-test-spec-failing]type[text/vnd.tiddlywiki-multiple]then[$:/core/ui/TestCaseTemplate]] stylesheet: [tag[$:/tags/Stylesheet]then[$:/core/ui/ViewTemplate/body/rendered-plain-text]] core-ui-tags: [tag[$:/tags/PageTemplate]] [tag[$:/tags/EditTemplate]] [tag[$:/tags/ViewTemplate]] [tag[$:/tags/KeyboardShortcut]] [tag[$:/tags/ImportPreview]] [tag[$:/tags/EditPreview]][tag[$:/tags/EditorToolbar]] [tag[$:/tags/Actions]] :then[[$:/core/ui/ViewTemplate/body/code]] system: [prefix[$:/boot/]] [prefix[$:/config/]] [prefix[$:/core/macros]] [prefix[$:/core/save/]] [prefix[$:/core/templates/]] [prefix[$:/info/]] [prefix[$:/language/]] [prefix[$:/languages/]] [prefix[$:/snippets/]] [prefix[$:/state/]] [prefix[$:/status/]] [prefix[$:/info/]] [prefix[$:/temp/]] +[!is[image]limit[1]then[$:/core/ui/ViewTemplate/body/code]] diff --git a/core/wiki/macros/testcase.tid b/core/wiki/macros/testcase.tid new file mode 100644 index 000000000..a04cb540d --- /dev/null +++ b/core/wiki/macros/testcase.tid @@ -0,0 +1,10 @@ +title: $:/core/macros/testcase +tags: $:/tags/Macro $:/tags/Global + +\whitespace trim + +\procedure testcase(tiddler) +<$tiddler tiddler=<>> +<$transclude $tiddler="$:/core/ui/TestCaseTemplate"> + +\end diff --git a/core/wiki/tags/ViewTemplateBodyFilter.tid b/core/wiki/tags/ViewTemplateBodyFilter.tid index 7b9fb7fd8..0143c1f88 100644 --- a/core/wiki/tags/ViewTemplateBodyFilter.tid +++ b/core/wiki/tags/ViewTemplateBodyFilter.tid @@ -1,2 +1,2 @@ title: $:/tags/ViewTemplateBodyFilter -list: $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/stylesheet $:/config/ViewTemplateBodyFilters/core-ui-advanced-search $:/config/ViewTemplateBodyFilters/core-ui-tags $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/default \ No newline at end of file +list: $:/config/ViewTemplateBodyFilters/testcase $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/stylesheet $:/config/ViewTemplateBodyFilters/core-ui-advanced-search $:/config/ViewTemplateBodyFilters/core-ui-tags $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/default \ No newline at end of file diff --git a/editions/test/tiddlers/HelloThere.tid b/editions/test/tiddlers/HelloThere.tid index d41f45fe2..74ea616e5 100644 --- a/editions/test/tiddlers/HelloThere.tid +++ b/editions/test/tiddlers/HelloThere.tid @@ -3,3 +3,7 @@ title: HelloThere This is TiddlyWiki's browser-based test runner for version <>. See the bottom of this page for the test results. https://tiddlywiki.com/ + +! Test Cases + +{{$:/core/ui/ControlPanel/TestCases}} diff --git a/editions/test/tiddlers/tests/from-tw5.com/tiddlywiki.files b/editions/test/tiddlers/tests/from-tw5.com/tiddlywiki.files new file mode 100644 index 000000000..c8ce1656e --- /dev/null +++ b/editions/test/tiddlers/tests/from-tw5.com/tiddlywiki.files @@ -0,0 +1,5 @@ +{ + "directories": [ + "../../../../tw5.com/tiddlers/testcases" + ] +} \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/concepts/CompoundTiddlers.tid b/editions/tw5.com/tiddlers/concepts/CompoundTiddlers.tid new file mode 100644 index 000000000..c4a049a6a --- /dev/null +++ b/editions/tw5.com/tiddlers/concepts/CompoundTiddlers.tid @@ -0,0 +1,31 @@ +title: CompoundTiddlers +modified: 20240507221902644 +created: 20240507221902644 +tags: Concepts + +Compound tiddlers are a special type of tiddler that can store one or more payload tiddlers. The tiddlers within a compound tiddler are only accessible via special operations, typically with the TestCaseWidget. + +The compound tiddler format is extremely simple, and includes the notable flaw that it does not permit tiddlers that contain a plus sign (`+`) on a line by itself. It is not intended as a general purpose way of storing tiddler data. + +Compound tiddlers are identified by having their type field set to `text/vnd.tiddlywiki-multiple`. + +The content of a compound tiddler consists of a sequence of tiddlers separated by a plus sign (`+`) on a line by itself. Each tiddler uses the same format as [[.tid files|TiddlerFiles]]. + +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 +``` diff --git a/editions/tw5.com/tiddlers/concepts/TestCaseTiddlers.tid b/editions/tw5.com/tiddlers/concepts/TestCaseTiddlers.tid new file mode 100644 index 000000000..ff84d6800 --- /dev/null +++ b/editions/tw5.com/tiddlers/concepts/TestCaseTiddlers.tid @@ -0,0 +1,27 @@ +title: TestCaseTiddlers +modified: 20240507221902644 +created: 20240507221902644 +tags: Concepts + +Test case tiddlers encapsulate one or more tiddlers that can be displayed as a [[test case|TestCaseWidget]]: an independent embedded wiki that can be used for testing or learning purposes. + +Test case tiddlers are formatted as CompoundTiddlers, allowing them to contain multiple tiddlers packed into one. + +Test case tiddlers have the following fields: + +|!Field |!Description | +|<<.field type>> | Should be set to `text/vnd.tiddlywiki-multiple` | +|<<.field tags>> | Test cases are tagged [[$:/tags/wiki-test-spec]]. Test cases that intentionally fail are tagged [[$:/tags/wiki-test-spec-failing]] | +|<<.field description>> |Descriptive heading for the test, intended to make it easy to identify the test | +|<<.field display-format>> |Optional, defaults to `wikitext`. Set to `plaintext` to cause the output to be rended as plain text | + +Test case tiddlers with the appropriate tag are shown in $:/ControlPanel + +Some payload tiddlers are set aside for special purposes: + +|!Tiddler |!Description | +|''Narrative'' |Narrative description of the test, intended to explain the purpose and operation of the test | +|''Output'' |The tiddler that produces the test output | +|''~ExpectedResult'' |HTML of expected result of rendering the ''Output'' tiddler | + + diff --git a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid index 91ee04e2f..956deec8c 100644 --- a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid +++ b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid @@ -5,7 +5,7 @@ tags: TableOfContents title: HelloThere type: text/vnd.tiddlywiki - !!.tc-hero-heading ''Welcome to TiddlyWiki, a unique [[non-linear|Philosophy of Tiddlers]] notebook for [[capturing|Creating and editing tiddlers]], [[organising|Structuring TiddlyWiki]] and [[sharing|Sharing your tiddlers with others]] complex information'' +!!.tc-hero-heading ''Welcome to TiddlyWiki, a unique [[non-linear|Philosophy of Tiddlers]] notebook for [[capturing|Creating and editing tiddlers]], [[organising|Structuring TiddlyWiki]] and [[sharing|Sharing your tiddlers with others]] complex information'' Use it to keep your [[to-do list|TaskManagementExample]], to plan an [[essay or novel|"TiddlyWiki for Scholars" by Alberto Molina]], or to organise your wedding. Record every thought that crosses your brain, or build a flexible and responsive website. diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/ImportCompound.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportCompound.tid new file mode 100644 index 000000000..20e967316 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportCompound.tid @@ -0,0 +1,33 @@ +title: TestCases/DataWidget/ImportCompound +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Importing a compound payload tiddler and adding custom fields +display-format: plaintext + +title: Narrative + +Using the data widget to import a tiddler stored in a compound tiddler ++ +title: Output + +<$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/tw5.com/tiddlers/testcases/DataWidget/ImportedFilter.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedFilter.tid new file mode 100644 index 000000000..3e7416fe3 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedFilter.tid @@ -0,0 +1,49 @@ +title: TestCases/DataWidget/ImportedFilter +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Imported filter definition +display-format: plaintext + +title: Narrative + +Using the data widget to create copies of all the tiddlers with the title prefix "Day: T", adding the field "custom" set to "Beta" ++ +title: Output + +<$data $filter="[prefix[Day: T]]" custom="Beta"/> ++ +title: Day: Monday +text: Today is Monday ++ +title: Day: Tuesday +text: Today is Tuesday ++ +title: Day: Wednesday +text: Today is Wednesday ++ +title: Day: Thursday +text: Today is Thursday ++ +title: Day: Friday +text: Today is Friday ++ +title: Day: Saturday +text: Today is Saturday ++ +title: Day: Sunday +text: Today is Sunday ++ +title: ExpectedResult + +

[ + { + "title": "Day: Thursday", + "text": "Today is Thursday", + "custom": "Beta" + }, + { + "title": "Day: Tuesday", + "text": "Today is Tuesday", + "custom": "Beta" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedTiddler.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedTiddler.tid new file mode 100644 index 000000000..02b89726c --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedTiddler.tid @@ -0,0 +1,29 @@ +title: TestCases/DataWidget/ImportedTiddler +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Imported tiddler definition +display-format: plaintext + +title: Narrative + +Using the data widget to create a tiddler that is a copy of the tiddler "Hello" with the addition of the field "custom" set to "Alpha" ++ +title: Output + +<$data $tiddler="Hello" custom="Alpha"/> ++ +title: Hello +modifier: JoeBloggs + +This is the Hello tiddler ++ +title: ExpectedResult + +

[ + { + "title": "Hello", + "modifier": "JoeBloggs", + "text": "This is the Hello tiddler", + "custom": "Alpha" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/Refreshing.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/Refreshing.tid new file mode 100644 index 000000000..92fc32090 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/Refreshing.tid @@ -0,0 +1,30 @@ +title: TestCases/DataWidget/Refreshing +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Refreshing the data widget +display-format: plaintext + +title: Narrative + +Verifying that the JSON output of the data widget is correctly refreshed when the data changes ++ +title: Output + +<$data title="Epsilon" text={{Subject}}/> ++ +title: Subject + +Nothing ++ +title: Actions + +<$action-setfield $tiddler="Subject" text="Theta"/> ++ +title: ExpectedResult + +

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

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/SimpleTiddler.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/SimpleTiddler.tid new file mode 100644 index 000000000..633ecb0b8 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/SimpleTiddler.tid @@ -0,0 +1,22 @@ +title: TestCases/DataWidget/SimpleTiddler +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Simple tiddler definition +display-format: plaintext + +title: Narrative + +Using the data widget to create a tiddler with the title "Epsilon" and the text "Theta" ++ +title: Output + +<$data title="Epsilon" text="Theta"/> ++ +title: ExpectedResult + +

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

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/TestCaseWidget/FailingTest.tid b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/FailingTest.tid new file mode 100644 index 000000000..bd9126e03 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/FailingTest.tid @@ -0,0 +1,15 @@ +title: TestCases/TestCaseWidget/FailingTest +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec-failing]] +description: An example of a failing test + +title: Narrative + +This test case intentionally fails to show how failures are displayed. ++ +title: Output + +The sum is <$text text={{{ [[2]add[2]] }}}/>. ++ +title: ExpectedResult +text:

The sum is not 8.

diff --git a/editions/tw5.com/tiddlers/testcases/TestCaseWidget/currentTiddler.tid b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/currentTiddler.tid new file mode 100644 index 000000000..824a2b17f --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/currentTiddler.tid @@ -0,0 +1,16 @@ +description: currentTiddler should be properly set +tags: $:/tags/wiki-test-spec +title: TestCases/TestCaseTiddler/currentTiddler +type: text/vnd.tiddlywiki-multiple + +title: Narrative + +currentTiddler variable in Output tiddler should be "Output" ++ +title: Output + +<$text text=<>> ++ +title: ExpectedResult + +

Output

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/TranscludeWidget/SimpleTransclusion.tid b/editions/tw5.com/tiddlers/testcases/TranscludeWidget/SimpleTransclusion.tid new file mode 100644 index 000000000..e7ef05e6d --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/TranscludeWidget/SimpleTransclusion.tid @@ -0,0 +1,23 @@ +title: TestCases/TranscludeWidget/SimpleTransclusion +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Simple transclusion + +title: Narrative + +This test case demonstrates transclusion of and links to other tiddlers. ++ +title: Output + +Good morning, my [[name|Name]] is {{Name}} and I [[live in|Address]] {{Address}} ++ +title: Name + +Robert Rabbit ++ +title: Address + +14 Carrot Street, Vegetabletown ++ +title: ExpectedResult +text:

Good morning, my name is Robert Rabbit and I live in 14 Carrot Street, Vegetabletown

diff --git a/editions/tw5.com/tiddlers/widgets/DataWidget.tid b/editions/tw5.com/tiddlers/widgets/DataWidget.tid new file mode 100644 index 000000000..8aaf1efaf --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/DataWidget.tid @@ -0,0 +1,42 @@ +caption: data +created: 20240507221902644 +modified: 20240507221902644 +tags: Widgets +title: DataWidget +type: text/vnd.tiddlywiki + +! Introduction + +The data widget is used with the <<.wlink TestCaseWidget>> widget and the [[Innerwiki Plugin]] to specify payload tiddlers that are to be included in the test case or innerwiki. + +! Content and Attributes + +The content of the data widget is ignored. It supports the following attributes: + +|!Attribute |!Description | +|<<.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 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 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. + +<> + +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 $. + +The attribute <<.attr $tiddler>> is used to ingest a single tiddler from the wiki containing the data widget: + +<> + +The attribute <<.attr $filter>> is used to ingest multiple tiddlers from the wiki containing the data widget: + +<> + +! Compound Tiddlers + +[[Compound tiddlers|CompoundTiddlers]] 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. + +<> diff --git a/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid b/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid new file mode 100644 index 000000000..a73403890 --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid @@ -0,0 +1,101 @@ +caption: testcase +created: 20240507221902644 +modified: 20240507221902644 +tags: Widgets +title: TestCaseWidget +type: text/vnd.tiddlywiki + +! Introduction + +The <<.wid testcase>> widget is designed to present interactive example test cases that are useful for learning and testing. It functions by creating an independent subwiki loaded with the specified payload tiddlers and then rendering a specified template from within the subwiki. The <<.wid testcase>> widget can optionally also be used to run and verify test results within the subwiki. + +This makes it possible to run independent tests that also serve as documentation examples. + +The <<.wid testcase>> widget can be used directly as documented below, but it is generally easier to create [[TestCaseTiddlers]]. These are special CompoundTiddlers that can contain multiple payload tiddlers making up a test case. + +!! Features + +Here is an example of a test case showing the default split view with the source tiddlers on the left and the tiddler titled `Output` rendered on the right. + +<> + +Notice also that clicking on links within the output pane will switch to the tab containing that tiddler. + +The text of the payload tiddlers listed on the left are editable, with the results being immediately reflected in the preview pane on the right. However, if the <<.wid testcase>> widget is refreshed then the modifications are lost. + +The green tick at the top left of a test case indicates that a test has been set up and that it passes. + +If the test fails, a red cross is shown, and there is a display of the differences between the actual results and the expected results: + +<> + +! Limitations + +The <<.wid testcase>> widget creates a lightweight TiddlyWiki environment that is a parasite of the main wiki. Because it is not a full, independent TiddlyWiki environment, there are some important limitations: + +* Output is rendered into a DIV, and so cannot be styled independently of the host wiki +* Any changes to the wiki made interactively by the user are volatile, and are lost when the <<.wid testcase>> widget is refreshed +* Startup actions are not supported +* Only plugins available in the host wiki can be included in the test case + +If these limitations are a problem, the [[Innerwiki Plugin]] offers the ability to embed a fully independent subwiki via an `