Skip to content
Snippets Groups Projects
Commit 78f0a96f authored by Marius Dumitru Florea's avatar Marius Dumitru Florea
Browse files

XWIKI-19113: Can't edit in place the Title and Content fields

parent f250bb5c
No related branches found
No related tags found
No related merge requests found
Showing
with 280 additions and 39 deletions
......@@ -61,4 +61,10 @@ class NestedLiveTableGeneratorIT extends LiveTableGeneratorIT
class NestedAddEntryIT extends AddEntryIT
{
}
@Nested
@DisplayName("Document fields test")
class NestedDocumentFieldsIT extends DocumentFieldsIT
{
}
}
......@@ -17,14 +17,13 @@
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.test.ui.appwithinminutes;
package org.xwiki.appwithinminutes.test.ui;
import java.util.Arrays;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.xwiki.appwithinminutes.test.po.ApplicationClassEditPage;
import org.xwiki.appwithinminutes.test.po.ApplicationCreatePage;
import org.xwiki.appwithinminutes.test.po.ApplicationHomeEditPage;
......@@ -33,26 +32,45 @@
import org.xwiki.appwithinminutes.test.po.EntryEditPage;
import org.xwiki.appwithinminutes.test.po.EntryNamePane;
import org.xwiki.model.reference.LocalDocumentReference;
import org.xwiki.test.ui.AbstractTest;
import org.xwiki.test.ui.AdminAuthenticationRule;
import org.xwiki.test.docker.junit5.UITest;
import org.xwiki.test.ui.TestUtils;
import org.xwiki.test.ui.po.EditablePropertyPane;
import org.xwiki.test.ui.po.LiveTableElement;
import org.xwiki.test.ui.po.ViewPage;
import org.xwiki.test.ui.po.editor.WikiEditPage;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Tests the special document fields available in the class editor, such as Title and Content.
*
* @version $Id$
* @since 4.5RC1
* @since 12.10.11
* @since 13.4.6
* @since 13.10RC1
*/
public class DocumentFieldsTest extends AbstractTest
@UITest
class DocumentFieldsIT
{
@Rule
public AdminAuthenticationRule adminAuthenticationRule = new AdminAuthenticationRule(true, getUtil());
@BeforeAll
static void beforeAll(TestUtils setup)
{
// By default the minimal distribution used for the tests doesn't have any rights setup. Let's create an Admin
// user part of the Admin Group and make sure that this Admin Group has admin rights in the wiki. We could also
// have given that Admin user the admin right directly but the solution we chose is closer to the XS
// distribution.
setup.loginAsSuperAdmin();
setup.setGlobalRights("XWiki.XWikiAdminGroup", "", "admin", true);
setup.createAdminUser();
setup.loginAsAdmin();
}
@Test
public void titleAndContent()
void titleAndContent(TestUtils setup)
{
setup.loginAsAdmin();
// Create a new application.
String appName = RandomStringUtils.randomAlphabetic(6);
ApplicationCreatePage appCreatePage = ApplicationCreatePage.gotoPage();
......@@ -88,37 +106,44 @@ public void titleAndContent()
EntryNamePane entryNamePane = homeEditPage.clickFinish().clickAddNewEntry();
entryNamePane.setName("Test");
EntryEditPage entryEditPage = entryNamePane.clickAdd();
Assert.assertEquals("13", entryEditPage.getValue("number1"));
assertEquals("13", entryEditPage.getValue("number1"));
// The page name is used as the default value for the title field.
Assert.assertEquals("Test", entryEditPage.getDocumentTitle());
Assert.assertEquals("Test", entryEditPage.getTitle());
assertEquals("Test", entryEditPage.getDocumentTitle());
assertEquals("Test", entryEditPage.getTitle());
entryEditPage.setTitle("Foo");
Assert.assertEquals(defaultContent, entryEditPage.getContent());
assertEquals(defaultContent, entryEditPage.getContent());
entryEditPage.setContent("Bar");
// Check that the title and the content of the entry have been updated.
ViewPage entryViewPage = entryEditPage.clickSaveAndView();
Assert.assertEquals("Foo", entryViewPage.getDocumentTitle());
Assert.assertTrue(entryViewPage.getContent().contains("Bar"));
entryViewPage.clickBreadcrumbLink(appName);
assertEquals("Foo", entryViewPage.getDocumentTitle());
assertTrue(entryViewPage.getContent().contains("Bar"));
// Verify that we can edit the document fields in-place.
String propertyReference = String.format("%s.Code.%1$sClass[0].title1", appName);
EditablePropertyPane<String> titleProperty = new EditablePropertyPane<>(propertyReference);
assertEquals("Foo", titleProperty.clickEdit().getValue());
titleProperty.setValue("Book").clickSave();
assertEquals("Book", titleProperty.getDisplayValue());
// Check the entries live table.
entryViewPage.clickBreadcrumbLink(appName);
LiveTableElement liveTable = new ApplicationHomePage().getEntriesLiveTable();
liveTable.waitUntilReady();
Assert.assertEquals(1, liveTable.getRowCount());
Assert.assertTrue(liveTable.hasRow("My Title", "Foo"));
Assert.assertTrue(liveTable.hasRow("My Content", "Bar"));
assertEquals(1, liveTable.getRowCount());
assertTrue(liveTable.hasRow("My Title", "Book"));
assertTrue(liveTable.hasRow("My Content", "Bar"));
// Check that the title and the content of the class have not been changed.
getUtil().gotoPage(new LocalDocumentReference(Arrays.asList(appName, "Code"), appName + "Class"), "edit",
setup.gotoPage(new LocalDocumentReference(Arrays.asList(appName, "Code"), appName + "Class"), "edit",
"editor=wiki");
WikiEditPage editPage = new WikiEditPage();
Assert.assertEquals(appName + " Class", editPage.getTitle());
Assert.assertEquals("", editPage.getContent());
assertEquals(appName + " Class", editPage.getTitle());
assertEquals("", editPage.getContent());
// Now edit the class and check if the default values for title and content are taken from the template.
editPage.editInline();
Assert.assertEquals(defaultTitle, new ClassFieldEditPane("title1").getDefaultValue());
Assert.assertEquals(defaultContent, new ClassFieldEditPane("content1").getDefaultValue());
editPage.edit();
assertEquals(defaultTitle, new ClassFieldEditPane("title1").getDefaultValue());
assertEquals(defaultContent, new ClassFieldEditPane("content1").getDefaultValue());
}
}
......@@ -20,7 +20,7 @@
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<xwikidoc version="1.3" reference="AppWithinMinutes.ClassSheetGenerator" locale="">
<xwikidoc version="1.4" reference="AppWithinMinutes.ClassSheetGenerator" locale="">
<web>AppWithinMinutes</web>
<name>ClassSheetGenerator</name>
<language/>
......@@ -47,7 +47,7 @@
#macro(displayProperty $property $indentation)
${indentation}&lt;dt ${escapetool.h}if (!${escapetool.d}editing &amp;&amp; ${escapetool.d}hasEdit)
${indentation} class="editableProperty"
${indentation} data-property="${escapetool.d}escapetool.xml(${escapetool.d}services.model.serialize(${escapetool.d}object.getProperty('#escapeSingleQuotes($property.name)').reference))"
${indentation} data-property="${escapetool.d}escapetool.xml(${escapetool.d}services.model.serialize(${escapetool.d}object.getPropertyReference('#escapeSingleQuotes($property.name)')))"
${indentation} data-property-type="object"${escapetool.h}end&gt;
${indentation} &lt;label${escapetool.h}if (${escapetool.d}editing) for="$escapetool.xml("${doc.fullName}_0_$property.name")"${escapetool.h}end&gt;
${indentation} ${escapetool.d}escapetool.xml(${escapetool.d}doc.displayPrettyName('#escapeSingleQuotes($property.name)', false, false))
......
......@@ -309,6 +309,22 @@ public PageAttachmentReference createPageAttachmentReference(PageReference pageR
return new PageAttachmentReference(fileName, pageReference);
}
/**
* Creates an {@link ObjectPropertyReference} from a property name and the reference of the object having that
* property.
*
* @param propertyName the property name
* @param objectReference the object reference
* @return the reference of the specified object property
* @since 12.10.11
* @since 13.4.6
* @since 13.10RC1
*/
public ObjectPropertyReference createObjectPropertyReference(String propertyName, ObjectReference objectReference)
{
return new ObjectPropertyReference(propertyName, objectReference);
}
/**
* Creates a {@link WikiReference} from a string representing the wiki name.
*
......
......@@ -35,6 +35,8 @@
import org.xwiki.model.reference.EntityReference;
import org.xwiki.model.reference.EntityReferenceResolver;
import org.xwiki.model.reference.EntityReferenceValueProvider;
import org.xwiki.model.reference.ObjectPropertyReference;
import org.xwiki.model.reference.ObjectReference;
import org.xwiki.model.reference.PageReference;
import org.xwiki.model.reference.PageReferenceResolver;
import org.xwiki.model.reference.SpaceReference;
......@@ -329,4 +331,13 @@ public void resolveClassPropertyWithHintAndParameters() throws Exception
assertEquals(reference,
this.service.resolveClassProperty("Class^property", "custom", parameters[0], parameters[1]));
}
@Test
void createObjectPropertyReference()
{
ObjectReference objectReference =
new ObjectReference("objectName", new DocumentReference("test", "Some", "Page"));
assertEquals(new ObjectPropertyReference("test", objectReference),
this.service.createObjectPropertyReference("test", objectReference));
}
}
......@@ -19,6 +19,8 @@
*/
package com.xpn.xwiki.api;
import org.xwiki.model.reference.ObjectPropertyReference;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
......@@ -126,4 +128,23 @@ public BaseObjectReference getReference()
{
return getBaseObject().getReference();
}
/**
* Helper method used to obtain the reference of an object property even when the object might not have the property
* (e.g. because it's a computed property which doesn't have a stored value so it's not saved on the object). This
* is a safe alternative to {@code getProperty(propertyName).getReference()} when you're not sure whether the object
* has the specified property or not.
*
* @param propertyName the property name
* @return the object property reference
* @see <a href="https://jira.xwiki.org/browse/XWIKI-19031">XWIKI-19031: Computed fields are stored as empty string
* properties in the database</a>
* @since 12.10.11
* @since 13.4.6
* @since 13.10RC1
*/
public ObjectPropertyReference getPropertyReference(String propertyName)
{
return new ObjectPropertyReference(propertyName, getReference());
}
}
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.api;
import javax.inject.Named;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.EntityReferenceSerializer;
import org.xwiki.model.reference.ObjectPropertyReference;
import org.xwiki.test.junit5.mockito.MockComponent;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.objects.BaseObject;
import com.xpn.xwiki.objects.BaseObjectReference;
import com.xpn.xwiki.test.junit5.mockito.OldcoreTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link Object}.
*
* @version $Id$
* @since 12.10.11
* @since 13.4.6
* @since 13.10RC1
*/
@OldcoreTest
class ObjectTest
{
private Object object;
@Mock
private BaseObject baseObject;
@Mock
private XWikiContext xcontext;
@MockComponent
@Named("compactwiki")
private EntityReferenceSerializer<String> compactWikiSerializer;
@BeforeEach
void setUp()
{
DocumentReference classReference = new DocumentReference("test", "Some", "Class");
DocumentReference documentReference = new DocumentReference("test", "Some", "Page");
when(this.compactWikiSerializer.serialize(classReference, documentReference)).thenReturn("Some.Class");
BaseObjectReference objectReference = new BaseObjectReference(classReference, 0, documentReference);
when(this.baseObject.getReference()).thenReturn(objectReference);
this.object = new Object(this.baseObject, this.xcontext);
}
@Test
void getPropertyReference()
{
assertEquals(new ObjectPropertyReference("age", this.baseObject.getReference()),
object.getPropertyReference("age"));
}
}
......@@ -26,7 +26,7 @@
* @since 12.6.3
* @since 12.9RC1
*/
public class DocumentSyntaxPropertyPane extends EditablePropertyPane
public class DocumentSyntaxPropertyPane extends EditablePropertyPane<String>
{
public DocumentSyntaxPropertyPane()
{
......
......@@ -27,11 +27,12 @@
/**
* The page object used to edit a document or object property in-place.
*
* @param <T> the property value type
* @version $Id$
* @since 12.6.3
* @since 12.9RC1
*/
public class EditablePropertyPane extends BaseElement
public class EditablePropertyPane<T> extends BaseElement
{
protected final WebElement element;
......@@ -47,8 +48,8 @@ public class EditablePropertyPane extends BaseElement
public EditablePropertyPane(String property)
{
this.element = getDriver().findElement(
By.cssSelector("dt.editableProperty[data-property=\"" + property + "\"]"));
this.element =
getDriver().findElement(By.cssSelector("dt.editableProperty[data-property=\"" + property + "\"]"));
this.viewer =
this.element.findElement(By.xpath("./following-sibling::dd[contains(@class, 'editableProperty-viewer')]"));
this.editor =
......@@ -58,21 +59,21 @@ public EditablePropertyPane(String property)
this.saveButton = this.element.findElement(By.className("editableProperty-save"));
}
public EditablePropertyPane clickEdit()
public EditablePropertyPane<T> clickEdit()
{
this.editButton.click();
getDriver().waitUntilCondition(visibilityOf(this.editor));
return this;
}
public EditablePropertyPane clickCancel()
public EditablePropertyPane<T> clickCancel()
{
this.cancelButton.click();
getDriver().waitUntilCondition(visibilityOf(this.editButton));
return this;
}
public EditablePropertyPane clickSave()
public EditablePropertyPane<T> clickSave()
{
this.saveButton.click();
getDriver().waitUntilCondition(visibilityOf(this.editButton));
......@@ -88,4 +89,51 @@ public String getDisplayValue()
{
return this.viewer.getText();
}
/**
* @return the property value while the property is being edited
* @since 12.10.11
* @since 13.4.6
* @since 13.10RC1
*/
@SuppressWarnings("unchecked")
public T getValue()
{
WebElement inputField = getInputField();
if ("checkbox".equals(inputField.getAttribute("type"))) {
return (T) Boolean.valueOf(inputField.isSelected());
} else {
return (T) inputField.getAttribute("value");
}
}
/**
* Sets the property value while the property is being edited.
*
* @param value the new property value
* @return this property pane
* @since 12.10.11
* @since 13.4.6
* @since 13.10RC1
*/
public EditablePropertyPane<T> setValue(T value)
{
WebElement inputField = getInputField();
if (value instanceof Boolean) {
// If the value is boolean then we assume the input field is a checkbox.
if (inputField.isSelected() != (Boolean) value) {
inputField.click();
}
} else {
// Assume the input field is a text field (text input or text area).
inputField.clear();
inputField.sendKeys(String.valueOf(value));
}
return this;
}
private WebElement getInputField()
{
return getDriver().findElementWithoutWaiting(this.editor, By.cssSelector("[name]"));
}
}
......@@ -19,12 +19,14 @@
*/
package org.xwiki.xclass.test.ui;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.xwiki.test.docker.junit5.TestReference;
import org.xwiki.test.docker.junit5.UITest;
import org.xwiki.test.ui.TestUtils;
import org.xwiki.test.ui.po.EditablePropertyPane;
import org.xwiki.test.ui.po.InlinePage;
import org.xwiki.test.ui.po.ViewPage;
import org.xwiki.test.ui.po.editor.ClassEditPage;
......@@ -55,7 +57,7 @@ void setup(TestUtils setup)
*/
@Test
@Order(1)
void createClass(TestUtils setup, TestReference reference)
void createClass(TestUtils setup, TestReference reference) throws Exception
{
//TODO: rewrite the test to not rely on the breadcrumb based on parent/child mechanism.
setup.setHierarchyMode("parentchild");
......@@ -89,6 +91,12 @@ void createClass(TestUtils setup, TestReference reference)
// Add a new property.
classEditor = classSheetPage.waitUntilPageIsLoaded().clickEditClassLink();
classEditor.addProperty("age", "Number").setPrettyName("Your current age");
// Add a computed property.
String titleDisplayer =
IOUtils.toString(this.getClass().getResourceAsStream("/contentDisplayer.wiki"), "UTF-8");
classEditor.addProperty("description", "ComputedField").setPrettyName("Description")
.setMetaProperty("customDisplay", titleDisplayer);
classEditor.clickSaveAndView();
// We have to wait for the page to load because Selenium doesn't do it all the time when we click on Save & View
......@@ -99,6 +107,7 @@ void createClass(TestUtils setup, TestReference reference)
// Assert that the properties are listed.
assertTrue(classSheetPage.hasProperty("color", "Your favorite color", "String"));
assertTrue(classSheetPage.hasProperty("age", "Your current age", "Number"));
assertTrue(classSheetPage.hasProperty("description", "Description", "Computed Field"));
// Create and bind a sheet.
classSheetPage = classSheetPage.clickCreateSheetButton().waitUntilPageIsLoaded()
......@@ -134,8 +143,21 @@ void createClass(TestUtils setup, TestReference reference)
editPage.setValue("age", "27");
ViewPage viewPage = editPage.clickSaveAndView();
// Verify that the properties can be edited in-place.
EditablePropertyPane<String> colorProperty =
new EditablePropertyPane<>(String.format("%s.%s[0].color", spaceName, classDocName));
assertEquals("blue", colorProperty.clickEdit().getValue());
colorProperty.setValue("pink").clickSave();
assertEquals("pink", colorProperty.getDisplayValue());
EditablePropertyPane<String> descriptionProperty =
new EditablePropertyPane<>(String.format("%s.%s[0].description", spaceName, classDocName));
assertEquals("", descriptionProperty.clickEdit().getValue());
descriptionProperty.setValue("Tester").clickSave();
assertEquals("Tester", descriptionProperty.getDisplayValue());
assertEquals(pageName, viewPage.getDocumentTitle());
assertEquals("YOUR FAVORITE COLOR\nblue\nYOUR CURRENT AGE\n27", viewPage.getContent());
assertEquals("YOUR FAVORITE COLOR\npink\nYOUR CURRENT AGE\n27\nDESCRIPTION\nTester", viewPage.getContent());
viewPage.clickBreadcrumbLink(classTitle);
classSheetPage.waitUntilPageIsLoaded();
......
{{velocity}}
#if ($type == 'edit')
{{html clean="false"}}
<textarea name="content">$!escapetool.xml($tdoc.content)</textarea>
{{/html}}
#elseif ("$!type" != '')
{{include reference="" /}}
#else
The display mode is not specified!
#end
{{/velocity}}
\ No newline at end of file
......@@ -60,7 +60,7 @@
#foreach ($property in $xclass.properties)
&lt;dt #if (!$editing &amp;&amp; $hasEdit)
class="editableProperty"
#set ($xobjectPropertyReference = $xobject.getProperty($property.name).reference)
#set ($xobjectPropertyReference = $xobject.getPropertyReference($property.name))
data-property="$escapetool.xml($services.model.serialize($xobjectPropertyReference))"
data-property-type="object"#end&gt;
## This must match the id generated by the $doc.display() method below.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment