Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add <$testcase> widget #7817

Merged
merged 43 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
497c56d
Initial Commit
Jermolene Oct 26, 2023
86507ba
Add note to preview build
Jermolene Oct 27, 2023
81f1994
Merge branch 'master' into testcase-widget
Jermolene Oct 31, 2023
5eae32d
Fix whitespace and indenting
Jermolene Oct 31, 2023
a32d514
Fix crash with unset $tiddler attribute on <$data> widget
Jermolene Oct 31, 2023
2c07310
Merge branch 'master' into testcase-widget
Jermolene Nov 21, 2023
4401081
Merge branch 'master' into testcase-widget
Jermolene Dec 11, 2023
f0d6779
Merge branch 'master' into testcase-widget
Jermolene May 2, 2024
17e939f
Don't duplicate "description" field in test cases
Jermolene May 2, 2024
22ad439
Use different background colours for nested testcase widgets
Jermolene May 5, 2024
57e74a0
Extend the testcase widget to run tests
Jermolene May 5, 2024
1be73ad
Add testcases to control panel
Jermolene May 5, 2024
fa9e643
Add a view template body template to render testcase tiddlers
Jermolene May 5, 2024
4758b6a
Test edition should display testcases
Jermolene May 5, 2024
d361a1e
Whitespace fixes
Jermolene May 5, 2024
39d4633
Make testcase tiddler tempalte link to itself
Jermolene May 5, 2024
0891331
Styling tweaks
Jermolene May 5, 2024
6b4bd47
Docs improvements
Jermolene May 5, 2024
e8a3ffd
Styling tweaks
Jermolene May 5, 2024
e378c6c
Run the new tw5.com testcases in the test edition
Jermolene May 6, 2024
1d89c79
Update data widget to display its content in JSON
Jermolene May 6, 2024
ff03a1b
Add testcase convenience procedure
Jermolene May 6, 2024
0db4c44
Clearer testcases for data widget, and docs tweaks
Jermolene May 6, 2024
43fe5e9
Don't expect our intentionally failing test to pass
Jermolene May 6, 2024
00ac550
Extend testcase default template so that the display format can be ch…
Jermolene May 6, 2024
d5b7bc7
DataWidget docs typo
Jermolene May 6, 2024
83ebfc6
Fix data widget not refreshing
Jermolene May 7, 2024
648855e
Links in testcase output switch to the tab containing that tiddler
Jermolene May 7, 2024
a856d0b
Docs update for 648855e8a50b1ee3bef3120b64b5713b69190c9b
Jermolene May 7, 2024
53908b7
Wording tweak
Jermolene May 8, 2024
8207b80
Add support for narrative tiddlers in test cases
Jermolene May 8, 2024
edc6661
Documentation improvements
Jermolene May 8, 2024
6b7cf37
Cleanup comments
Jermolene May 8, 2024
400514f
Remove obsolete code comments
Jermolene May 8, 2024
70f68c7
Simplify template
Jermolene May 8, 2024
4f19f0a
Docs update
Jermolene May 8, 2024
5c34d27
Rename $:/core/ui/testcases/DefaultTemplate/SourceTabs from $:/core/u…
Jermolene May 8, 2024
3751a88
Use the view template body for failing tests
Jermolene May 8, 2024
5a7f958
Don't reference the geospatial plugin
Jermolene May 8, 2024
4234aa9
"Test case" should be two words
Jermolene May 9, 2024
eb4e9d8
Fix handling of currentTiddler variable
Jermolene May 15, 2024
7ca70c9
Merge branch 'master' into testcase-widget
Jermolene May 20, 2024
769969f
Prepare for merging
Jermolene May 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
139 changes: 139 additions & 0 deletions core/modules/widgets/data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*\
title: $:/core/modules/widgets/data.js
type: application/javascript
module-type: widget

Widget to represent a single item of data

\*/
(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();
this.renderChildren(parent,nextSibling);
};

/*
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) {
tiddlers.push(this.wiki.getTiddler(title));
}
}
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) {
// Refresh our attributes
var changedAttributes = this.computeAttributes();
// Refresh our children, and indicate that we refreshed if any of our attribute values have changed
return this.refreshChildren(changedTiddlers) || $tw.utils.count(changedAttributes) > 0;
};

exports.data = DataWidget;

})();
113 changes: 113 additions & 0 deletions core/modules/widgets/testcase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*\
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 the test case template
// loadTiddler(this.testcaseTemplate);
// 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 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");
};

/*
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;

})();
15 changes: 15 additions & 0 deletions core/modules/widgets/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,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
*/
Expand Down
22 changes: 22 additions & 0 deletions core/ui/TestCases/DefaultTemplate.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
title: $:/core/ui/testcases/DefaultTemplate

\whitespace trim
Jermolene marked this conversation as resolved.
Show resolved Hide resolved
<$let
state={{{ [<qualify "$:/state/testcase">] }}}
>
<div class="tc-testcase-wrapper">
<div class="tc-testcase-header">
<h2><$transclude tiddler="Description" mode="inline"/></h2>
</div>
<div class="tc-testcase-panes">
<div class="tc-testcase-source">
<$macrocall $name="tabs" tabsList="[all[tiddlers]sort[]] -[prefix<state>] -Description -ExpectedResult -Output Output +[putfirst[]] -[has[plugin-type]]" state=<<state>> default="Output" template="$:/core/ui/testcases/DefaultTemplate/Source"/>
</div>
<div class="tc-testcase-divider">
</div>
<div class="tc-testcase-output">
<$transclude tiddler="Output"/>
</div>
</div>
</div>
</$let>
19 changes: 19 additions & 0 deletions core/ui/TestCases/DefaultTemplateSource.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
title: $:/core/ui/testcases/DefaultTemplate/Source
Jermolene marked this conversation as resolved.
Show resolved Hide resolved

<$list filter="[<currentTab>fields[]] -text +[limit[1]]" variable="ignore">
<table class="tc-field-table">
<tbody>
<$list filter="[<currentTab>fields[]sort[]] -text -title title +[putfirst[]]" variable="fieldName">
<tr>
<td>
<$text text=<<fieldName>>/>
</td>
<td>
<$view tiddler=<<currentTab>> field=<<fieldName>>/>
</td>
</tr>
</$list>
</tbody>
</table>
</$list>
<$edit class="tc-edit-texteditor" tiddler=<<currentTab>>/>
4 changes: 4 additions & 0 deletions core/ui/TestCases/RawJSONTemplate.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
title: $:/core/ui/testcases/RawJSONTemplate

\whitespace trim
<$text text=<<payloadTiddlers>>/>
3 changes: 2 additions & 1 deletion editions/prerelease/tiddlywiki.info
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"tiddlywiki/dynannotate",
"tiddlywiki/codemirror",
"tiddlywiki/menubar",
"tiddlywiki/jszip"
"tiddlywiki/jszip",
"tiddlywiki/innerwiki"
],
"themes": [
"tiddlywiki/vanilla",
Expand Down
27 changes: 27 additions & 0 deletions editions/test/tiddlers/tests/data/data-widget/ImportCompound.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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
<$testcase template="$:/core/ui/testcases/RawJSONTemplate">
<$data $compound-tiddler="Compound" custom="Alpha"/>
</$testcase>
+
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

<p><div><div>[{"title":"Payload Tiddler","tags":"Alpha Beta Gamma","text":"This is a payload tiddler from a compound tiddler","custom":"Alpha"}]</div></div></p>
28 changes: 28 additions & 0 deletions editions/test/tiddlers/tests/data/data-widget/ImportFilter.tid
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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
<$testcase template="$:/core/ui/testcases/RawJSONTemplate">
<$data $filter="[tag[Definitions]]" custom="Alpha"/>
</$testcase>
+
title: HelloThere
tags: Definitions

This is the tiddler HelloThere
+
title: AnotherDefinition
tags: Definitions

This is the tiddler AnotherDefinition
+
title: ExpectedResult

<p><div><div>[{"title":"AnotherDefinition","tags":"Definitions","text":"This is the tiddler AnotherDefinition","custom":"Alpha"},{"title":"HelloThere","tags":"Definitions","text":"This is the tiddler HelloThere","custom":"Alpha"}]</div></div></p>