mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-09-30 07:50:47 +00:00
Merge branch 'master' into demo-alternate-store
This commit is contained in:
commit
9493084f95
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
- master
|
- master
|
||||||
- tiddlywiki-com
|
- tiddlywiki-com
|
||||||
env:
|
env:
|
||||||
NODE_VERSION: "12"
|
NODE_VERSION: "18"
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -14,7 +14,13 @@ jobs:
|
|||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: "${{ env.NODE_VERSION }}"
|
node-version: "${{ env.NODE_VERSION }}"
|
||||||
- run: "./bin/test.sh"
|
- run: "./bin/ci-test.sh"
|
||||||
|
- uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: playwright-report/
|
||||||
|
retention-days: 30
|
||||||
build-prerelease:
|
build-prerelease:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,4 +5,6 @@
|
|||||||
tmp/
|
tmp/
|
||||||
output/
|
output/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
16
bin/ci-test.sh
Executable file
16
bin/ci-test.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# test TiddlyWiki5 for tiddlywiki.com
|
||||||
|
|
||||||
|
node ./tiddlywiki.js \
|
||||||
|
./editions/test \
|
||||||
|
--verbose \
|
||||||
|
--version \
|
||||||
|
--rendertiddler $:/core/save/all test.html text/plain \
|
||||||
|
--test \
|
||||||
|
|| exit 1
|
||||||
|
|
||||||
|
npm install playwright @playwright/test
|
||||||
|
npx playwright install chromium firefox --with-deps
|
||||||
|
|
||||||
|
npx playwright test
|
@ -58,6 +58,7 @@ Last entry/entries in list
|
|||||||
exports.last = function(source,operator,options) {
|
exports.last = function(source,operator,options) {
|
||||||
var count = $tw.utils.getInt(operator.operand,1),
|
var count = $tw.utils.getInt(operator.operand,1),
|
||||||
results = [];
|
results = [];
|
||||||
|
if(count === 0) return results;
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
results.push(title);
|
results.push(title);
|
||||||
});
|
});
|
||||||
|
@ -823,8 +823,8 @@ exports.hashString = function(str) {
|
|||||||
Base64 utility functions that work in either browser or Node.js
|
Base64 utility functions that work in either browser or Node.js
|
||||||
*/
|
*/
|
||||||
if(typeof window !== 'undefined') {
|
if(typeof window !== 'undefined') {
|
||||||
exports.btoa = window.btoa;
|
exports.btoa = function(binstr) { return window.btoa(binstr); }
|
||||||
exports.atob = window.atob;
|
exports.atob = function(b64) { return window.atob(b64); }
|
||||||
} else {
|
} else {
|
||||||
exports.btoa = function(binstr) {
|
exports.btoa = function(binstr) {
|
||||||
return Buffer.from(binstr, 'binary').toString('base64');
|
return Buffer.from(binstr, 'binary').toString('base64');
|
||||||
|
@ -28,6 +28,18 @@ Inherit from the base widget class
|
|||||||
*/
|
*/
|
||||||
ListWidget.prototype = new Widget();
|
ListWidget.prototype = new Widget();
|
||||||
|
|
||||||
|
ListWidget.prototype.initialise = function(parseTreeNode,options) {
|
||||||
|
// Bail if parseTreeNode is undefined, meaning that the ListWidget constructor was called without any arguments so that it can be subclassed
|
||||||
|
if(parseTreeNode === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// First call parent constructor to set everything else up
|
||||||
|
Widget.prototype.initialise.call(this,parseTreeNode,options);
|
||||||
|
// Now look for <$list-template> and <$list-empty> widgets as immediate child widgets
|
||||||
|
// This is safe to do during initialization because parse trees never change after creation
|
||||||
|
this.findExplicitTemplates();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Render this widget into the DOM
|
Render this widget into the DOM
|
||||||
*/
|
*/
|
||||||
@ -68,8 +80,6 @@ ListWidget.prototype.execute = function() {
|
|||||||
this.counterName = this.getAttribute("counter");
|
this.counterName = this.getAttribute("counter");
|
||||||
this.storyViewName = this.getAttribute("storyview");
|
this.storyViewName = this.getAttribute("storyview");
|
||||||
this.historyTitle = this.getAttribute("history");
|
this.historyTitle = this.getAttribute("history");
|
||||||
// Look for <$list-template> and <$list-empty> widgets as immediate child widgets
|
|
||||||
this.findExplicitTemplates();
|
|
||||||
// Compose the list elements
|
// Compose the list elements
|
||||||
this.list = this.getTiddlerList();
|
this.list = this.getTiddlerList();
|
||||||
var members = [],
|
var members = [],
|
||||||
@ -92,6 +102,7 @@ ListWidget.prototype.findExplicitTemplates = function() {
|
|||||||
var self = this;
|
var self = this;
|
||||||
this.explicitListTemplate = null;
|
this.explicitListTemplate = null;
|
||||||
this.explicitEmptyTemplate = null;
|
this.explicitEmptyTemplate = null;
|
||||||
|
this.hasTemplateInBody = false;
|
||||||
var searchChildren = function(childNodes) {
|
var searchChildren = function(childNodes) {
|
||||||
$tw.utils.each(childNodes,function(node) {
|
$tw.utils.each(childNodes,function(node) {
|
||||||
if(node.type === "list-template") {
|
if(node.type === "list-template") {
|
||||||
@ -100,6 +111,8 @@ ListWidget.prototype.findExplicitTemplates = function() {
|
|||||||
self.explicitEmptyTemplate = node.children;
|
self.explicitEmptyTemplate = node.children;
|
||||||
} else if(node.type === "element" && node.tag === "p") {
|
} else if(node.type === "element" && node.tag === "p") {
|
||||||
searchChildren(node.children);
|
searchChildren(node.children);
|
||||||
|
} else {
|
||||||
|
self.hasTemplateInBody = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -160,11 +173,11 @@ ListWidget.prototype.makeItemTemplate = function(title,index) {
|
|||||||
// Check for a <$list-item> widget
|
// Check for a <$list-item> widget
|
||||||
if(this.explicitListTemplate) {
|
if(this.explicitListTemplate) {
|
||||||
templateTree = this.explicitListTemplate;
|
templateTree = this.explicitListTemplate;
|
||||||
} else if (!this.explicitEmptyTemplate) {
|
} else if(this.hasTemplateInBody) {
|
||||||
templateTree = this.parseTreeNode.children;
|
templateTree = this.parseTreeNode.children;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!templateTree) {
|
if(!templateTree || templateTree.length === 0) {
|
||||||
// Default template is a link to the title
|
// Default template is a link to the title
|
||||||
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [
|
||||||
{type: "text", text: title}
|
{type: "text", text: title}
|
||||||
@ -414,4 +427,27 @@ ListItemWidget.prototype.refresh = function(changedTiddlers) {
|
|||||||
|
|
||||||
exports.listitem = ListItemWidget;
|
exports.listitem = ListItemWidget;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Make <$list-template> and <$list-empty> widgets that do nothing
|
||||||
|
*/
|
||||||
|
var ListTemplateWidget = function(parseTreeNode,options) {
|
||||||
|
// Main initialisation inherited from widget.js
|
||||||
|
this.initialise(parseTreeNode,options);
|
||||||
|
};
|
||||||
|
ListTemplateWidget.prototype = new Widget();
|
||||||
|
ListTemplateWidget.prototype.render = function() {}
|
||||||
|
ListTemplateWidget.prototype.refresh = function() { return false; }
|
||||||
|
|
||||||
|
exports["list-template"] = ListTemplateWidget;
|
||||||
|
|
||||||
|
var ListEmptyWidget = function(parseTreeNode,options) {
|
||||||
|
// Main initialisation inherited from widget.js
|
||||||
|
this.initialise(parseTreeNode,options);
|
||||||
|
};
|
||||||
|
ListEmptyWidget.prototype = new Widget();
|
||||||
|
ListEmptyWidget.prototype.render = function() {}
|
||||||
|
ListEmptyWidget.prototype.refresh = function() { return false; }
|
||||||
|
|
||||||
|
exports["list-empty"] = ListEmptyWidget;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -89,7 +89,7 @@ $value={{{ [subfilter<get-field-value-tiddler-filter>get[text]] }}}/>
|
|||||||
</td>
|
</td>
|
||||||
<td class="tc-edit-field-remove">
|
<td class="tc-edit-field-remove">
|
||||||
<$button class="tc-btn-invisible" tooltip={{$:/language/EditTemplate/Field/Remove/Hint}} aria-label={{$:/language/EditTemplate/Field/Remove/Caption}}>
|
<$button class="tc-btn-invisible" tooltip={{$:/language/EditTemplate/Field/Remove/Hint}} aria-label={{$:/language/EditTemplate/Field/Remove/Caption}}>
|
||||||
<$action-deletefield $field=<<currentField>>/><$set name="currentTiddlerCSSescaped" value={{{ [<currentTiddler>escapecss[]] }}}><$action-sendmessage $message="tm-focus-selector" $param=<<current-tiddler-new-field-selector>>/></$set>
|
<$action-deletefield $field=<<currentField>>/>
|
||||||
{{$:/core/images/delete-button}}
|
{{$:/core/images/delete-button}}
|
||||||
</$button>
|
</$button>
|
||||||
</td>
|
</td>
|
||||||
|
@ -118,7 +118,7 @@ tags: $:/tags/Macro
|
|||||||
<$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item" >
|
<$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item" >
|
||||||
<li class=<<toc-item-class>>>
|
<li class=<<toc-item-class>>>
|
||||||
<$link to={{{ [<currentTiddler>get[target]else<currentTiddler>] }}}>
|
<$link to={{{ [<currentTiddler>get[target]else<currentTiddler>] }}}>
|
||||||
<$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}</$button>">
|
<$list filter="[all[current]tagging[]$sort$limit[1]] -[subfilter<__exclude__>]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}</$button>">
|
||||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||||
<$transclude tiddler=<<toc-closed-icon>> />
|
<$transclude tiddler=<<toc-closed-icon>> />
|
||||||
@ -145,7 +145,7 @@ tags: $:/tags/Macro
|
|||||||
<$qualify name="toc-state" title={{{ [[$:/state/toc]addsuffix<__path__>addsuffix[-]addsuffix<currentTiddler>] }}}>
|
<$qualify name="toc-state" title={{{ [[$:/state/toc]addsuffix<__path__>addsuffix[-]addsuffix<currentTiddler>] }}}>
|
||||||
<$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item">
|
<$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item">
|
||||||
<li class=<<toc-item-class>>>
|
<li class=<<toc-item-class>>>
|
||||||
<$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="""<$button class="tc-btn-invisible">{{$:/core/images/blank}}</$button><span class="toc-item-muted"><<toc-caption>></span>""">
|
<$list filter="[all[current]tagging[]$sort$limit[1]] -[subfilter<__exclude__>]" variable="ignore" emptyMessage="""<$button class="tc-btn-invisible">{{$:/core/images/blank}}</$button><span class="toc-item-muted"><<toc-caption>></span>""">
|
||||||
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
<$reveal type="nomatch" stateTitle=<<toc-state>> text="open">
|
||||||
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
<$button setTitle=<<toc-state>> setTo="open" class="tc-btn-invisible tc-popup-keep">
|
||||||
<$transclude tiddler=<<toc-closed-icon>> />
|
<$transclude tiddler=<<toc-closed-icon>> />
|
||||||
|
25
editions/test/playwright.spec.js
Normal file
25
editions/test/playwright.spec.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const { test, expect } = require('@playwright/test');
|
||||||
|
const {resolve} = require('path');
|
||||||
|
|
||||||
|
const indexPath = resolve(__dirname, 'output', 'test.html');
|
||||||
|
const crossPlatformIndexPath = indexPath.replace(/^\/+/, '');
|
||||||
|
|
||||||
|
|
||||||
|
test('get started link', async ({ page }) => {
|
||||||
|
// The tests can take a while to run
|
||||||
|
const timeout = 1000 * 30;
|
||||||
|
test.setTimeout(timeout);
|
||||||
|
|
||||||
|
// Load the generated test TW html
|
||||||
|
await page.goto(`file:///${crossPlatformIndexPath}`);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
await expect(page.locator('.tc-site-title'), "Expected correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5');
|
||||||
|
|
||||||
|
// Wait for jasmine results bar to appear
|
||||||
|
await expect(page.locator('.jasmine-overall-result'), "Expected jasmine test results bar to be present").toBeVisible({timeout});
|
||||||
|
|
||||||
|
// Assert the tests have passed
|
||||||
|
await expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Expected jasmine tests to not have failed").not.toBeVisible();
|
||||||
|
await expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Expected jasmine tests to have passed").toBeVisible();
|
||||||
|
});
|
@ -365,6 +365,7 @@ Tests the filtering mechanism.
|
|||||||
expect(wiki.filterTiddlers("[sort[title]first[8]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three");
|
expect(wiki.filterTiddlers("[sort[title]first[8]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three");
|
||||||
expect(wiki.filterTiddlers("[sort[title]first[x]]").join(",")).toBe("$:/ShadowPlugin");
|
expect(wiki.filterTiddlers("[sort[title]first[x]]").join(",")).toBe("$:/ShadowPlugin");
|
||||||
expect(wiki.filterTiddlers("[sort[title]last[]]").join(",")).toBe("TiddlerOne");
|
expect(wiki.filterTiddlers("[sort[title]last[]]").join(",")).toBe("TiddlerOne");
|
||||||
|
expect(wiki.filterTiddlers("[sort[title]last[0]]").join(",")).toBe("");
|
||||||
expect(wiki.filterTiddlers("[sort[title]last[2]]").join(",")).toBe("Tiddler Three,TiddlerOne");
|
expect(wiki.filterTiddlers("[sort[title]last[2]]").join(",")).toBe("Tiddler Three,TiddlerOne");
|
||||||
expect(wiki.filterTiddlers("[sort[title]last[8]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne");
|
expect(wiki.filterTiddlers("[sort[title]last[8]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne");
|
||||||
expect(wiki.filterTiddlers("[sort[title]last[x]]").join(",")).toBe("TiddlerOne");
|
expect(wiki.filterTiddlers("[sort[title]last[x]]").join(",")).toBe("TiddlerOne");
|
||||||
|
46
playwright.config.js
Normal file
46
playwright.config.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
const { defineConfig, devices } = require('@playwright/test');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://playwright.dev/docs/test-configuration
|
||||||
|
*/
|
||||||
|
module.exports = defineConfig({
|
||||||
|
testDir: './editions/test/',
|
||||||
|
|
||||||
|
// Allow parallel tests
|
||||||
|
fullyParallel: true,
|
||||||
|
|
||||||
|
// Prevent accidentally committed "test.only" from wrecking havoc
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
|
||||||
|
// Do not retry tests on failure
|
||||||
|
retries: 0,
|
||||||
|
|
||||||
|
// How many parallel workers
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
|
||||||
|
// Reporter to use. See https://playwright.dev/docs/test-reporters
|
||||||
|
reporter: 'html',
|
||||||
|
|
||||||
|
// Settings shared with all the tests
|
||||||
|
use: {
|
||||||
|
// Take a screenshot when the test fails
|
||||||
|
screenshot: {
|
||||||
|
mode: 'only-on-failure',
|
||||||
|
fullPage: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: 'firefox',
|
||||||
|
use: { ...devices['Desktop Firefox'] },
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
@ -56,6 +56,11 @@ name: tiddlywiki
|
|||||||
rendering-intent: auto;
|
rendering-intent: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tc-tiddler-frame .tc-tiddler-editor .tc-edit-texteditor,
|
||||||
|
.tc-tiddler-frame .tc-tiddler-editor .tc-tiddler-preview-preview {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.cm-s-tiddlywiki.CodeMirror, .cm-s-tiddlywiki .CodeMirror-gutters { background-color: <<colour tiddler-editor-background>>; color: <<colour foreground>>; }
|
.cm-s-tiddlywiki.CodeMirror, .cm-s-tiddlywiki .CodeMirror-gutters { background-color: <<colour tiddler-editor-background>>; color: <<colour foreground>>; }
|
||||||
.cm-s-tiddlywiki .CodeMirror-gutters {background: <<colour tiddler-editor-background>>; border-right: 1px solid <<colour tiddler-editor-border>>;}
|
.cm-s-tiddlywiki .CodeMirror-gutters {background: <<colour tiddler-editor-background>>; border-right: 1px solid <<colour tiddler-editor-border>>;}
|
||||||
.cm-s-tiddlywiki .CodeMirror-linenumber {color: <<colour foreground>>;}
|
.cm-s-tiddlywiki .CodeMirror-linenumber {color: <<colour foreground>>;}
|
||||||
|
Loading…
Reference in New Issue
Block a user