Add $timestamp argument for <$action-deletefield> widget (#8115)

* Start on some tests for <$action-deletefield />

* Only update modified field if we actually delete a field

…in the <$action-deletefield /> widget.

Fixes a bug where <$action-deletefield foo /> would update the modified field
if the "foo" field wasn't present on a tiddler.

* action-deletefield: Test when modified does and doesn't exist

* Add $timestamp argument to action-deletefield

To make it more consistent with other tiddler-manipulating action widgets

* Add docs for action-deletefield $timestamp
This commit is contained in:
Rob Hoelz 2024-04-04 10:03:15 -05:00 committed by GitHub
parent 105e8195d5
commit e9aa3c6c93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 184 additions and 2 deletions

View File

@ -37,6 +37,7 @@ Compute the internal state of the widget
DeleteFieldWidget.prototype.execute = function() {
this.actionTiddler = this.getAttribute("$tiddler",this.getVariable("currentTiddler"));
this.actionField = this.getAttribute("$field",null);
this.actionTimestamp = this.getAttribute("$timestamp","yes") === "yes";
};
/*
@ -69,11 +70,15 @@ DeleteFieldWidget.prototype.invokeAction = function(triggeringWidget,event) {
$tw.utils.each(this.attributes,function(attribute,name) {
if(name.charAt(0) !== "$" && name !== "title") {
removeFields[name] = undefined;
hasChanged = true;
if(name in tiddler.fields) {
hasChanged = true;
}
}
});
if(hasChanged) {
this.wiki.addTiddler(new $tw.Tiddler(this.wiki.getCreationFields(),tiddler,removeFields,this.wiki.getModificationFields()));
var creationFields = this.actionTimestamp ? this.wiki.getCreationFields() : {};
var modificationFields = this.actionTimestamp ? this.wiki.getModificationFields() : {};
this.wiki.addTiddler(new $tw.Tiddler(creationFields,tiddler,removeFields,modificationFields));
}
}
return true; // Action was invoked

View File

@ -0,0 +1,176 @@
/*\
title: test-action-deletefield.js
type: application/javascript
tags: [[$:/tags/test-spec]]
Tests <$action-deletefield />.
\*/
(function(){
/* jslint node: true, browser: true */
/* eslint-env node, browser, jasmine */
/* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/
/* global $tw, require */
"use strict";
describe("<$action-deletefield /> tests", function() {
const TEST_TIDDLER_TITLE = "TargetTiddler";
const TEST_TIDDLER_MODIFIED = "20240313114828368";
function setupWiki(condition, targetField, wikiOptions) {
// Create a wiki
var wiki = new $tw.Wiki({});
var tiddlers = [{
title: "Root",
text: "Some dummy content"
}];
var tiddler;
if(condition.targetTiddlerExists) {
var fields = {
title: TEST_TIDDLER_TITLE,
};
if(condition.modifiedFieldExists) {
fields.modified = TEST_TIDDLER_MODIFIED;
}
if(condition.targetFieldExists) {
fields[targetField] = "some text";
}
var tiddler = new $tw.Tiddler(fields);
tiddlers.push(tiddler);
}
wiki.addTiddlers(tiddlers);
wiki.addIndexersToWiki();
var widgetNode = wiki.makeTranscludeWidget("Root",{document: $tw.fakeDocument, parseAsInline: true});
var container = $tw.fakeDocument.createElement("div");
widgetNode.render(container,null);
return {
wiki: wiki,
widgetNode: widgetNode,
contaienr: container,
tiddler: tiddler,
};
}
function generateTestConditions() {
var conditions = [];
$tw.utils.each([true, false], function(tiddlerArgumentIsPresent) {
$tw.utils.each([true, false], function(targetTiddlerExists) {
$tw.utils.each([true, false], function(targetFieldExists) {
$tw.utils.each([true, false], function(fieldArgumentIsUsed) {
$tw.utils.each([true, false], function(modifiedFieldExists) {
$tw.utils.each(["", "yes", "no"], function(timestampArgument) {
conditions.push({
tiddlerArgumentIsPresent: tiddlerArgumentIsPresent,
targetTiddlerExists: targetTiddlerExists,
targetFieldExists: targetFieldExists,
fieldArgumentIsUsed: fieldArgumentIsUsed,
modifiedFieldExists: modifiedFieldExists,
timestampArgument: timestampArgument,
});
});
});
});
});
});
});
return conditions;
}
function generateActionWikitext(condition, targetField) {
var actionPieces = [
"<$action-deletefield",
(condition.tiddlerArgumentIsPresent ? "$tiddler='" + TEST_TIDDLER_TITLE + "'" : ""),
(condition.fieldArgumentIsUsed ? "$field='" + targetField + "'" : targetField),
(condition.timestampArgument !== "" ? "$timestamp='" + condition.timestampArgument + "'" : ""),
"/>",
];
return actionPieces.join(" ");
}
function generateTestContext(action, tiddler) {
var expectationContext = "action: " + action + "\ntiddler:\n\n";
if(tiddler) {
expectationContext += tiddler.getFieldStringBlock({exclude: ["text"]});
if(tiddler.text) {
expectationContext += "\n\n" + tiddler.text;
}
expectationContext += "\n\n";
} else {
expectationContext += "null";
}
return expectationContext;
}
it("should correctly delete fields", function() {
var fields = ['caption', 'description', 'text'];
var conditions = generateTestConditions();
$tw.utils.each(conditions, function(condition) {
$tw.utils.each(fields, function(field) {
var info = setupWiki(condition, field);
var originalTiddler = info.tiddler;
var invokeActions = function(actions) {
info.widgetNode.invokeActionString(actions,info.widgetNode,null,{
currentTiddler: TEST_TIDDLER_TITLE,
});
};
var action = generateActionWikitext(condition,field);
invokeActions(action);
var testContext = generateTestContext(action,originalTiddler);
var tiddler = info.wiki.getTiddler(TEST_TIDDLER_TITLE);
if(originalTiddler) {
// assert that the tiddler doesn't have the target field anymore
expect(tiddler.hasField(field)).withContext(testContext).toBeFalsy();
var targetFieldWasPresent = originalTiddler.hasField(field);
var updateTimestamps = condition.timestampArgument !== "no";
// "created" should exist if it did beforehand, or if the tiddler changed and we asked the widget to update timestamps
var createdFieldShouldExist = originalTiddler.hasField("created") || (targetFieldWasPresent && updateTimestamps);
// "created" should change only if it didn't exist beforehand and the tiddler changed and we asked the widget to update timestamps
var createdFieldShouldChange = !originalTiddler.hasField("created") && (targetFieldWasPresent && updateTimestamps);
// "modified" should exist if it did beforehand, or if the tiddler changed and we asked the widget to update timestamps
var modifiedFieldShouldExist = originalTiddler.hasField("modified") || (targetFieldWasPresent && updateTimestamps);
// "modified" should change if the tiddler changed and we asked the widget to update timestamps
var modifiedFieldShouldChange = targetFieldWasPresent && updateTimestamps;
expect(tiddler.hasField("created")).withContext(testContext).toBe(createdFieldShouldExist);
expect(tiddler.hasField("modified")).withContext(testContext).toBe(modifiedFieldShouldExist);
if(createdFieldShouldChange) {
expect(tiddler.fields.created).withContext(testContext).not.toEqual(originalTiddler.fields.created);
} else {
expect(tiddler.fields.created).withContext(testContext).toEqual(originalTiddler.fields.created);
}
if(modifiedFieldShouldChange) {
expect(tiddler.fields.modified).withContext(testContext).not.toEqual(originalTiddler.fields.modified);
} else {
expect(tiddler.fields.modified).withContext(testContext).toEqual(originalTiddler.fields.modified);
}
} else {
// assert that the tiddler didn't get created if it didn't exist already
expect(tiddler).withContext(testContext).toBeUndefined();
}
});
});
});
});
})();

View File

@ -16,6 +16,7 @@ The ''action-deletefield'' widget is invisible. Any content within it is ignored
|!Attribute |!Description |
|$tiddler |The title of the tiddler whose fields are to be modified (if not provided defaults to the [[current tiddler|Current Tiddler]]) |
|$field |Optional name of a field to delete |
|$timestamp |<<.from-version "5.3.4">> Specifies whether the timestamp(s) of the target tiddler will be updated (''modified'' and ''modifier'', plus ''created'' and ''creator'' for newly created tiddlers). Can be "yes" (the default) or "no" |
|//{any attributes not starting with $}// |Each attribute name specifies a field to be deleted. The attribute value is ignored and need not be specified |
! Examples