1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.test.ui

File TestUtils.java

 

Coverage histogram

../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

162
579
214
4
2,447
1,521
314
0.54
2.71
53.5
1.47

Classes

Class Line # Actions
TestUtils 100 356 0% 209 87
0.8552412485.5%
TestUtils.Session 1054 5 0% 4 0
1.0100%
TestUtils.RestTestUtils 1757 216 0% 100 76
0.777777877.8%
TestUtils.RestTestUtils.ResourceAPI 1763 2 0% 1 0
1.0100%
 

Contributing tests

This file is covered by 266 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.test.ui;
21   
22    import java.io.ByteArrayInputStream;
23    import java.io.ByteArrayOutputStream;
24    import java.io.File;
25    import java.io.FileInputStream;
26    import java.io.InputStream;
27    import java.net.URI;
28    import java.net.URLEncoder;
29    import java.util.ArrayList;
30    import java.util.Arrays;
31    import java.util.Collections;
32    import java.util.HashMap;
33    import java.util.HashSet;
34    import java.util.IdentityHashMap;
35    import java.util.List;
36    import java.util.Locale;
37    import java.util.Map;
38    import java.util.Set;
39    import java.util.regex.Matcher;
40    import java.util.regex.Pattern;
41   
42    import javax.ws.rs.core.MediaType;
43    import javax.ws.rs.core.Response.Status;
44    import javax.ws.rs.core.UriBuilder;
45    import javax.xml.bind.JAXBContext;
46    import javax.xml.bind.JAXBException;
47    import javax.xml.bind.Marshaller;
48    import javax.xml.bind.Unmarshaller;
49   
50    import org.apache.commons.httpclient.HttpClient;
51    import org.apache.commons.httpclient.HttpMethod;
52    import org.apache.commons.httpclient.UsernamePasswordCredentials;
53    import org.apache.commons.httpclient.auth.AuthScope;
54    import org.apache.commons.httpclient.methods.DeleteMethod;
55    import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
56    import org.apache.commons.httpclient.methods.GetMethod;
57    import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
58    import org.apache.commons.httpclient.methods.PostMethod;
59    import org.apache.commons.httpclient.methods.PutMethod;
60    import org.apache.commons.httpclient.methods.RequestEntity;
61    import org.apache.commons.io.IOUtils;
62    import org.apache.commons.lang.ArrayUtils;
63    import org.apache.commons.lang.StringUtils;
64    import org.junit.Assert;
65    import org.openqa.selenium.By;
66    import org.openqa.selenium.Cookie;
67    import org.openqa.selenium.NoSuchElementException;
68    import org.openqa.selenium.WebDriver;
69    import org.openqa.selenium.WebElement;
70    import org.openqa.selenium.support.ui.ExpectedCondition;
71    import org.xwiki.component.manager.ComponentManager;
72    import org.xwiki.model.EntityType;
73    import org.xwiki.model.reference.AttachmentReference;
74    import org.xwiki.model.reference.DocumentReference;
75    import org.xwiki.model.reference.EntityReference;
76    import org.xwiki.model.reference.EntityReferenceResolver;
77    import org.xwiki.model.reference.EntityReferenceSerializer;
78    import org.xwiki.model.reference.LocalDocumentReference;
79    import org.xwiki.rest.model.jaxb.Page;
80    import org.xwiki.rest.model.jaxb.Property;
81    import org.xwiki.rest.model.jaxb.Xwiki;
82    import org.xwiki.rest.resources.attachments.AttachmentResource;
83    import org.xwiki.rest.resources.classes.ClassPropertyResource;
84    import org.xwiki.rest.resources.objects.ObjectPropertyResource;
85    import org.xwiki.rest.resources.objects.ObjectResource;
86    import org.xwiki.rest.resources.objects.ObjectsResource;
87    import org.xwiki.rest.resources.pages.PageResource;
88    import org.xwiki.rest.resources.pages.PageTranslationResource;
89    import org.xwiki.test.integration.XWikiExecutor;
90    import org.xwiki.test.ui.po.ViewPage;
91    import org.xwiki.test.ui.po.editor.ClassEditPage;
92    import org.xwiki.test.ui.po.editor.ObjectEditPage;
93   
94    /**
95    * Helper methods for testing, not related to a specific Page Object. Also made available to tests classes.
96    *
97    * @version $Id: a806f31eb5ba4c08c29829271d99c959ba7bb336 $
98    * @since 3.2M3
99    */
 
100    public class TestUtils
101    {
102    /**
103    * @since 5.0M2
104    */
105    public static final UsernamePasswordCredentials ADMIN_CREDENTIALS =
106    new UsernamePasswordCredentials("Admin", "admin");
107   
108    /**
109    * @since 5.1M1
110    */
111    public static final UsernamePasswordCredentials SUPER_ADMIN_CREDENTIALS =
112    new UsernamePasswordCredentials("superadmin", "pass");
113   
114    /**
115    * @since 5.0M2
116    * @deprecated since 7.3M1, use {@link #getBaseURL()} instead
117    */
118    @Deprecated
119    public static final String BASE_URL = XWikiExecutor.URL + ":" + XWikiExecutor.DEFAULT_PORT + "/xwiki/";
120   
121    /**
122    * @since 5.0M2
123    * @deprecated since 7.3M1, use {@link #getBaseBinURL()} instead
124    */
125    @Deprecated
126    public static final String BASE_BIN_URL = BASE_URL + "bin/";
127   
128    /**
129    * @since 5.0M2
130    * @deprecated since 7.3M1, use {@link #getBaseURL()} instead
131    */
132    @Deprecated
133    public static final String BASE_REST_URL = BASE_URL + "rest/";
134   
135    /**
136    * @since 7.3M1
137    */
138    public static final int[] STATUS_OK_NOT_FOUND =
139    new int[] { Status.OK.getStatusCode(), Status.NOT_FOUND.getStatusCode() };
140   
141    /**
142    * @since 7.3M1
143    */
144    public static final int[] STATUS_OK = new int[] { Status.OK.getStatusCode() };
145   
146    /**
147    * @since 7.3M1
148    */
149    public static final int[] STATUS_NO_CONTENT = new int[] { Status.NO_CONTENT.getStatusCode() };
150   
151    /**
152    * @since 8.3RC1
153    */
154    public static final int[] STATUS_NO_CONTENT_NOT_FOUND =
155    new int[] { Status.NO_CONTENT.getStatusCode(), Status.NOT_FOUND.getStatusCode() };
156   
157    /**
158    * @since 7.3M1
159    */
160    public static final int[] STATUS_CREATED_ACCEPTED =
161    new int[] { Status.CREATED.getStatusCode(), Status.ACCEPTED.getStatusCode() };
162   
163    /**
164    * @since 7.3M1
165    */
166    public static final int[] STATUS_CREATED = new int[] { Status.CREATED.getStatusCode() };
167   
168    private static PersistentTestContext context;
169   
170    private static ComponentManager componentManager;
171   
172    private static EntityReferenceResolver<String> relativeReferenceResolver;
173   
174    private static EntityReferenceSerializer<String> referenceSerializer;
175   
176    private static EntityReferenceResolver<String> referenceResolver;
177   
178    /**
179    * Used to convert Java object into its REST XML representation.
180    */
181    private static Marshaller marshaller;
182   
183    /**
184    * Used to convert REST request XML result into its Java representation.
185    */
186    private static Unmarshaller unmarshaller;
187   
188    /** Cached secret token. TODO cache for each user. */
189    private String secretToken = null;
190   
191    private HttpClient httpClient;
192   
193    /**
194    * @since 8.0M1
195    */
196    private List<XWikiExecutor> executors;
197   
198    /**
199    * @since 7.3M1
200    */
201    private int currentExecutorIndex;
202   
203    /**
204    * @since 7.3M1
205    */
206    private String currentWiki = "xwiki";
207   
208    private RestTestUtils rest;
209   
 
210  117 toggle public TestUtils()
211    {
212  117 this.httpClient = new HttpClient();
213  117 this.httpClient.getState().setCredentials(AuthScope.ANY, SUPER_ADMIN_CREDENTIALS);
214  117 this.httpClient.getParams().setAuthenticationPreemptive(true);
215   
216  117 this.rest = new RestTestUtils(this);
217    }
218   
219    /**
220    * @since 8.0M1
221    */
 
222  20290 toggle public XWikiExecutor getCurrentExecutor()
223    {
224  20290 return this.executors != null && this.executors.size() > this.currentExecutorIndex
225    ? this.executors.get(this.currentExecutorIndex) : null;
226    }
227   
228    /**
229    * @since 8.0M1
230    */
 
231  586 toggle public void switchExecutor(int index)
232    {
233  586 this.currentExecutorIndex = index;
234    }
235   
236    /**
237    * @since 8.0M1
238    */
 
239  32 toggle public void setExecutors(List<XWikiExecutor> executors)
240    {
241  32 this.executors = executors;
242    }
243   
244    /** Used so that AllTests can set the persistent test context. */
 
245  32 toggle public static void setContext(PersistentTestContext context)
246    {
247  32 TestUtils.context = context;
248    }
249   
 
250  33 toggle public static void initializeComponent(ComponentManager componentManager) throws Exception
251    {
252  33 TestUtils.componentManager = componentManager;
253  33 TestUtils.relativeReferenceResolver =
254    TestUtils.componentManager.getInstance(EntityReferenceResolver.TYPE_STRING, "relative");
255  33 TestUtils.referenceResolver = TestUtils.componentManager.getInstance(EntityReferenceResolver.TYPE_STRING);
256  33 TestUtils.referenceSerializer = TestUtils.componentManager.getInstance(EntityReferenceSerializer.TYPE_STRING);
257    }
258   
 
259  4947 toggle public XWikiWebDriver getDriver()
260    {
261  4947 return context.getDriver();
262    }
263   
 
264  32 toggle public Session getSession()
265    {
266  32 return this.new Session(getDriver().manage().getCookies(), getSecretToken());
267    }
268   
 
269  107 toggle public void setSession(Session session)
270    {
271  107 WebDriver.Options options = getDriver().manage();
272  107 options.deleteAllCookies();
273  107 if (session != null) {
274  38 for (Cookie cookie : session.getCookies()) {
275  153 options.addCookie(cookie);
276    }
277    }
278  107 if (session != null && !StringUtils.isEmpty(session.getSecretToken())) {
279  38 this.secretToken = session.getSecretToken();
280    } else {
281  69 recacheSecretToken();
282    }
283    }
284   
285    /**
286    * @since 7.0RC1
287    */
 
288  89 toggle public void setDefaultCredentials(String username, String password)
289    {
290  89 setDefaultCredentials(new UsernamePasswordCredentials(username, password));
291    }
292   
293    /**
294    * @since 7.0RC1
295    */
 
296  160 toggle public UsernamePasswordCredentials setDefaultCredentials(UsernamePasswordCredentials defaultCredentials)
297    {
298  160 UsernamePasswordCredentials currentCredentials = getDefaultCredentials();
299   
300  160 this.httpClient.getState().setCredentials(AuthScope.ANY, defaultCredentials);
301   
302  160 return currentCredentials;
303    }
304   
 
305  164 toggle public UsernamePasswordCredentials getDefaultCredentials()
306    {
307  164 return (UsernamePasswordCredentials) this.httpClient.getState().getCredentials(AuthScope.ANY);
308    }
309   
 
310  6 toggle public void loginAsSuperAdmin()
311    {
312  6 login(SUPER_ADMIN_CREDENTIALS.getUserName(), SUPER_ADMIN_CREDENTIALS.getPassword());
313    }
314   
 
315  4 toggle public void loginAsSuperAdminAndGotoPage(String pageURL)
316    {
317  4 loginAndGotoPage(SUPER_ADMIN_CREDENTIALS.getUserName(), SUPER_ADMIN_CREDENTIALS.getPassword(), pageURL);
318    }
319   
 
320  3 toggle public void loginAsAdmin()
321    {
322  3 login(ADMIN_CREDENTIALS.getUserName(), ADMIN_CREDENTIALS.getPassword());
323    }
324   
 
325  0 toggle public void loginAsAdminAndGotoPage(String pageURL)
326    {
327  0 loginAndGotoPage(ADMIN_CREDENTIALS.getUserName(), ADMIN_CREDENTIALS.getPassword(), pageURL);
328    }
329   
 
330  178 toggle public void login(String username, String password)
331    {
332  178 loginAndGotoPage(username, password, null);
333    }
334   
 
335  184 toggle public void loginAndGotoPage(String username, String password, String pageURL)
336    {
337  184 if (!username.equals(getLoggedInUserName())) {
338    // Log in and direct to a non existent page so that it loads very fast and we don't incur the time cost of
339    // going to the home page for example.
340    // Also recache the CSRF token
341  53 getDriver().get(getURLToLoginAndGotoPage(username, password, getURL("XWiki", "Register", "register")));
342  53 recacheSecretTokenWhenOnRegisterPage();
343  53 if (pageURL != null) {
344    // Go to the page asked
345  6 getDriver().get(pageURL);
346    } else {
347  47 getDriver().get(getURLToNonExistentPage());
348    }
349   
350  53 setDefaultCredentials(username, password);
351    }
352    }
353   
354    /**
355    * Consider using setSession(null) because it will drop the cookies which is faster than invoking a logout action.
356    */
 
357  0 toggle public String getURLToLogout()
358    {
359  0 return getURL("XWiki", "XWikiLogin", "logout");
360    }
361   
 
362  7 toggle public String getURLToLoginAsAdmin()
363    {
364  7 return getURLToLoginAs(ADMIN_CREDENTIALS.getUserName(), ADMIN_CREDENTIALS.getPassword());
365    }
366   
 
367  0 toggle public String getURLToLoginAsSuperAdmin()
368    {
369  0 return getURLToLoginAs(SUPER_ADMIN_CREDENTIALS.getUserName(), SUPER_ADMIN_CREDENTIALS.getPassword());
370    }
371   
 
372  12 toggle public String getURLToLoginAs(final String username, final String password)
373    {
374  12 return getURLToLoginAndGotoPage(username, password, null);
375    }
376   
377    /**
378    * @param pageURL the URL of the page to go to after logging in.
379    * @return URL to accomplish login and goto.
380    */
 
381  36 toggle public String getURLToLoginAsAdminAndGotoPage(final String pageURL)
382    {
383  36 return getURLToLoginAndGotoPage(ADMIN_CREDENTIALS.getUserName(), ADMIN_CREDENTIALS.getPassword(), pageURL);
384    }
385   
386    /**
387    * @param pageURL the URL of the page to go to after logging in.
388    * @return URL to accomplish login and goto.
389    */
 
390  0 toggle public String getURLToLoginAsSuperAdminAndGotoPage(final String pageURL)
391    {
392  0 return getURLToLoginAndGotoPage(SUPER_ADMIN_CREDENTIALS.getUserName(), SUPER_ADMIN_CREDENTIALS.getPassword(),
393    pageURL);
394    }
395   
396    /**
397    * @param username the name of the user to log in as.
398    * @param password the password for the user to log in.
399    * @param pageURL the URL of the page to go to after logging in.
400    * @return URL to accomplish login and goto.
401    */
 
402  134 toggle public String getURLToLoginAndGotoPage(final String username, final String password, final String pageURL)
403    {
404  134 Map<String, String> parameters = new HashMap<String, String>()
405    {
 
406  134 toggle {
407  134 put("j_username", username);
408  134 put("j_password", password);
409  134 if (pageURL != null && pageURL.length() > 0) {
410  122 put("xredirect", pageURL);
411    }
412    }
413    };
414  134 return getURL("XWiki", "XWikiLogin", "loginsubmit", parameters);
415    }
416   
417    /**
418    * @return URL to a non existent page that loads very fast (we are using plain mode so that we don't even have to
419    * display the skin ;))
420    */
 
421  115 toggle public String getURLToNonExistentPage()
422    {
423  115 return getURL("NonExistentSpace", "NonExistentPage", "view", "xpage=plain");
424    }
425   
426    /**
427    * After successful completion of this function, you are guaranteed to be logged in as the given user and on the
428    * page passed in pageURL.
429    */
 
430  4 toggle public void assertOnPage(final String pageURL)
431    {
432  4 final String pageURI = pageURL.replaceAll("\\?.*", "");
433  4 getDriver().waitUntilCondition(new ExpectedCondition<Boolean>()
434    {
 
435  4 toggle @Override
436    public Boolean apply(WebDriver driver)
437    {
438  4 return getDriver().getCurrentUrl().contains(pageURI);
439    }
440    });
441    }
442   
 
443  185 toggle public String getLoggedInUserName()
444    {
445  185 By userAvatarInDrawer = By.id("tmUser");
446  185 if (!getDriver().hasElementWithoutWaiting(userAvatarInDrawer)) {
447    // Guest
448  39 return null;
449    }
450   
451  146 WebElement element = getDriver().findElementWithoutWaiting(userAvatarInDrawer);
452  146 String href = element.getAttribute("href");
453  146 String loggedInUserName = href.substring(href.lastIndexOf("/") + 1);
454   
455    // Return
456  146 return loggedInUserName;
457    }
458   
 
459  33 toggle public void createUserAndLogin(final String username, final String password, Object... properties)
460    {
461  33 createUserAndLoginWithRedirect(username, password, getURLToNonExistentPage(), properties);
462    }
463   
 
464  33 toggle public void createUserAndLoginWithRedirect(final String username, final String password, String url,
465    Object... properties)
466    {
467  33 createUser(username, password, getURLToLoginAndGotoPage(username, password, url), properties);
468   
469  33 setDefaultCredentials(username, password);
470    }
471   
 
472  39 toggle public void createUser(final String username, final String password, String redirectURL, Object... properties)
473    {
474  39 Map<String, String> parameters = new HashMap<String, String>();
475  39 parameters.put("register", "1");
476  39 parameters.put("xwikiname", username);
477  39 parameters.put("register_password", password);
478  39 parameters.put("register2_password", password);
479  39 parameters.put("register_email", "");
480  39 parameters.put("xredirect", redirectURL);
481  39 parameters.put("form_token", getSecretToken());
482  39 getDriver().get(getURL("XWiki", "Register", "register", parameters));
483  39 recacheSecretToken();
484  39 if (properties.length > 0) {
485  9 updateObject("XWiki", username, "XWiki.XWikiUsers", 0, properties);
486    }
487    }
488   
 
489  132 toggle public ViewPage gotoPage(String space, String page)
490    {
491  132 gotoPage(space, page, "view");
492  132 return new ViewPage();
493    }
494   
495    /**
496    * @since 7.2M2
497    */
 
498  18 toggle public ViewPage gotoPage(EntityReference reference)
499    {
500  18 gotoPage(reference, "view");
501  18 return new ViewPage();
502    }
503   
 
504  769 toggle public void gotoPage(String space, String page, String action)
505    {
506  769 gotoPage(space, page, action, "");
507    }
508   
509    /**
510    * @since 7.2M2
511    */
 
512  23 toggle public void gotoPage(EntityReference reference, String action)
513    {
514  23 gotoPage(reference, action, "");
515    }
516   
517    /**
518    * @since 3.5M1
519    */
 
520  32 toggle public void gotoPage(String space, String page, String action, Object... queryParameters)
521    {
522  32 gotoPage(space, page, action, toQueryString(queryParameters));
523    }
524   
 
525  16 toggle public void gotoPage(String space, String page, String action, Map<String, ?> queryParameters)
526    {
527  16 gotoPage(Collections.singletonList(space), page, action, queryParameters);
528    }
529   
530    /**
531    * @since 7.2M2
532    */
 
533  124 toggle public void gotoPage(List<String> spaces, String page, String action, Map<String, ?> queryParameters)
534    {
535  124 gotoPage(spaces, page, action, toQueryString(queryParameters));
536    }
537   
538    /**
539    * @since 7.2M2
540    */
 
541  85 toggle public void gotoPage(EntityReference reference, String action, Map<String, ?> queryParameters)
542    {
543  85 gotoPage(reference, action, toQueryString(queryParameters));
544    }
545   
 
546  927 toggle public void gotoPage(String space, String page, String action, String queryString)
547    {
548  927 gotoPage(Collections.singletonList(space), page, action, queryString);
549    }
550   
551    /**
552    * @since 7.2M2
553    */
 
554  1055 toggle public void gotoPage(List<String> spaces, String page, String action, String queryString)
555    {
556  1055 gotoPage(getURL(spaces, page, action, queryString));
557    }
558   
559    /**
560    * @since 7.2M2
561    */
 
562  110 toggle public void gotoPage(EntityReference reference, String action, String queryString)
563    {
564  110 gotoPage(getURL(reference, action, queryString));
565   
566    // Update current wiki
567  110 EntityReference wikiReference = reference.extractReference(EntityType.WIKI);
568  110 if (wikiReference != null) {
569  32 this.currentWiki = wikiReference.getName();
570    }
571    }
572   
 
573  1225 toggle public void gotoPage(String url)
574    {
575    // Only navigate if the current URL is different from the one to go to, in order to improve performances.
576  1225 if (!getDriver().getCurrentUrl().equals(url)) {
577  1217 getDriver().get(url);
578    }
579    }
580   
 
581  34 toggle public String getURLToDeletePage(String space, String page)
582    {
583  34 return getURL(space, page, "delete", "confirm=1");
584    }
585   
586    /**
587    * @since 7.2M2
588    */
 
589  3 toggle public String getURLToDeletePage(EntityReference reference)
590    {
591  3 return getURL(reference, "delete", "confirm=1");
592    }
593   
594    /**
595    * @param space the name of the space to delete
596    * @return the URL that can be used to delete the specified pace
597    * @since 4.5
598    */
 
599  29 toggle public String getURLToDeleteSpace(String space)
600    {
601  29 return getURL(space, "WebHome", "deletespace", "confirm=1&async=false&affectChidlren=on");
602    }
603   
 
604  62 toggle public ViewPage createPage(String space, String page, String content, String title)
605    {
606  62 return createPage(Collections.singletonList(space), page, content, title);
607    }
608   
609    /**
610    * @since 7.2M2
611    */
 
612  9 toggle public ViewPage createPage(EntityReference reference, String content, String title)
613    {
614  9 return createPage(reference, content, title, null);
615    }
616   
617    /**
618    * @since 7.2M2
619    */
 
620  68 toggle public ViewPage createPage(List<String> spaces, String page, String content, String title)
621    {
622  68 return createPage(spaces, page, content, title, null);
623    }
624   
 
625  9 toggle public ViewPage createPage(String space, String page, String content, String title, String syntaxId)
626    {
627  9 return createPage(Collections.singletonList(space), page, content, title, syntaxId);
628    }
629   
630    /**
631    * @since 7.2M2
632    */
 
633  9 toggle public ViewPage createPage(EntityReference reference, String content, String title, String syntaxId)
634    {
635  9 return createPage(reference, content, title, syntaxId, null);
636    }
637   
638    /**
639    * @since 7.2M2
640    */
 
641  77 toggle public ViewPage createPage(List<String> spaces, String page, String content, String title, String syntaxId)
642    {
643  77 return createPage(spaces, page, content, title, syntaxId, null);
644    }
645   
 
646  2 toggle public ViewPage createPage(String space, String page, String content, String title, String syntaxId,
647    String parentFullPageName)
648    {
649  2 return createPage(Collections.singletonList(space), page, content, title, syntaxId, parentFullPageName);
650    }
651   
652    /**
653    * @since 7.2M2
654    */
 
655  80 toggle public ViewPage createPage(List<String> spaces, String page, String content, String title, String syntaxId,
656    String parentFullPageName)
657    {
658  80 Map<String, String> queryMap = new HashMap<String, String>();
659  80 if (content != null) {
660  58 queryMap.put("content", content);
661    }
662  80 if (title != null) {
663  56 queryMap.put("title", title);
664    }
665  80 if (syntaxId != null) {
666  10 queryMap.put("syntaxId", syntaxId);
667    }
668  80 if (parentFullPageName != null) {
669  2 queryMap.put("parent", parentFullPageName);
670    }
671  80 gotoPage(spaces, page, "save", queryMap);
672  80 return new ViewPage();
673    }
674   
675    /**
676    * @since 7.2M2
677    */
 
678  9 toggle public ViewPage createPage(EntityReference reference, String content, String title, String syntaxId,
679    String parentFullPageName)
680    {
681  9 Map<String, String> queryMap = new HashMap<>();
682  9 if (content != null) {
683  9 queryMap.put("content", content);
684    }
685  9 if (title != null) {
686  9 queryMap.put("title", title);
687    }
688  9 if (syntaxId != null) {
689  0 queryMap.put("syntaxId", syntaxId);
690    }
691  9 if (parentFullPageName != null) {
692  0 queryMap.put("parent", parentFullPageName);
693    }
694  9 gotoPage(reference, "save", queryMap);
695  9 return new ViewPage();
696    }
697   
698    /**
699    * @since 5.1M2
700    */
 
701  0 toggle public ViewPage createPageWithAttachment(String space, String page, String content, String title, String syntaxId,
702    String parentFullPageName, String attachmentName, InputStream attachmentData) throws Exception
703    {
704  0 return createPageWithAttachment(space, page, content, title, syntaxId, parentFullPageName, attachmentName,
705    attachmentData, null);
706    }
707   
708    /**
709    * @since 5.1M2
710    */
 
711  0 toggle public ViewPage createPageWithAttachment(String space, String page, String content, String title, String syntaxId,
712    String parentFullPageName, String attachmentName, InputStream attachmentData,
713    UsernamePasswordCredentials credentials) throws Exception
714    {
715  0 return createPageWithAttachment(Collections.singletonList(space), page, content, title, syntaxId,
716    parentFullPageName, attachmentName, attachmentData, credentials);
717    }
718   
719    /**
720    * @since 7.2M2
721    */
 
722  1 toggle public ViewPage createPageWithAttachment(List<String> spaces, String page, String content, String title,
723    String syntaxId, String parentFullPageName, String attachmentName, InputStream attachmentData,
724    UsernamePasswordCredentials credentials) throws Exception
725    {
726  1 ViewPage vp = createPage(spaces, page, content, title, syntaxId, parentFullPageName);
727  1 attachFile(spaces, page, attachmentName, attachmentData, false, credentials);
728  1 return vp;
729    }
730   
731    /**
732    * @since 5.1M2
733    */
 
734  0 toggle public ViewPage createPageWithAttachment(String space, String page, String content, String title,
735    String attachmentName, InputStream attachmentData) throws Exception
736    {
737  0 return createPageWithAttachment(space, page, content, title, null, null, attachmentName, attachmentData);
738    }
739   
740    /**
741    * @since 5.1M2
742    */
 
743  2 toggle public ViewPage createPageWithAttachment(String space, String page, String content, String title,
744    String attachmentName, InputStream attachmentData, UsernamePasswordCredentials credentials) throws Exception
745    {
746  2 ViewPage vp = createPage(space, page, content, title);
747  2 attachFile(space, page, attachmentName, attachmentData, false, credentials);
748  2 return vp;
749    }
750   
 
751  34 toggle public void deletePage(String space, String page)
752    {
753  34 getDriver().get(getURLToDeletePage(space, page));
754    }
755   
756    /**
757    * @since 7.2M2
758    */
 
759  3 toggle public void deletePage(EntityReference reference)
760    {
761  3 getDriver().get(getURLToDeletePage(reference));
762    }
763   
764    /**
765    * @since 7.2M2
766    */
 
767  8 toggle public EntityReference resolveDocumentReference(String referenceAsString)
768    {
769  8 return referenceResolver.resolve(referenceAsString, EntityType.DOCUMENT);
770    }
771   
772    /**
773    * @since 7.2M3
774    */
 
775  6 toggle public EntityReference resolveSpaceReference(String referenceAsString)
776    {
777  6 return referenceResolver.resolve(referenceAsString, EntityType.SPACE);
778    }
779   
780    /**
781    * @since 7.2RC1
782    */
 
783  21 toggle public String serializeReference(EntityReference reference)
784    {
785  21 return referenceSerializer.serialize(reference);
786    }
787   
788    /**
789    * Accesses the URL to delete the specified space.
790    *
791    * @param space the name of the space to delete
792    * @since 4.5
793    */
 
794  29 toggle public void deleteSpace(String space)
795    {
796  29 getDriver().get(getURLToDeleteSpace(space));
797    }
798   
 
799  111 toggle public boolean pageExists(String space, String page) throws Exception
800    {
801  111 return rest().exists(new LocalDocumentReference(space, page));
802    }
803   
804    /**
805    * @since 7.2M2
806    */
 
807  4 toggle public boolean pageExists(List<String> spaces, String page) throws Exception
808    {
809  4 return rest().exists(new LocalDocumentReference(spaces, page));
810    }
811   
812    /**
813    * Get the URL to view a page.
814    *
815    * @param space the space in which the page resides.
816    * @param page the name of the page.
817    */
 
818  36 toggle public String getURL(String space, String page)
819    {
820  36 return getURL(space, page, "view");
821    }
822   
823    /**
824    * Get the URL of an action on a page.
825    *
826    * @param space the space in which the page resides.
827    * @param page the name of the page.
828    * @param action the action to do on the page.
829    */
 
830  91 toggle public String getURL(String space, String page, String action)
831    {
832  91 return getURL(space, page, action, "");
833    }
834   
835    /**
836    * Get the URL of an action on a page with a specified query string.
837    *
838    * @param space the space in which the page resides.
839    * @param page the name of the page.
840    * @param action the action to do on the page.
841    * @param queryString the query string to pass in the URL.
842    */
 
843  480 toggle public String getURL(String space, String page, String action, String queryString)
844    {
845  480 return getURL(action, new String[] { space, page }, queryString);
846    }
847   
848    /**
849    * @since 7.3M1
850    */
 
851  3 toggle public String getURL(List<String> spaces, String page)
852    {
853  3 return getURL(spaces, page, "view", "");
854    }
855   
856    /**
857    * @since 7.2M2
858    */
 
859  1058 toggle public String getURL(List<String> spaces, String page, String action, String queryString)
860    {
861  1058 List<String> path = new ArrayList<>(spaces);
862  1058 path.add(page);
863  1058 return getURL(action, path.toArray(new String[] {}), queryString);
864    }
865   
866    /**
867    * @since 7.2M2
868    */
 
869  113 toggle public String getURL(EntityReference reference, String action, String queryString)
870    {
871  113 return getURL(action, extractListFromReference(reference).toArray(new String[] {}), queryString);
872    }
873   
874    /**
875    * @since 7.2M2
876    */
 
877  15 toggle public String getURLFragment(EntityReference reference)
878    {
879  15 return StringUtils.join(extractListFromReference(reference), "/");
880    }
881   
 
882  128 toggle private List<String> extractListFromReference(EntityReference reference)
883    {
884  128 List<String> path = new ArrayList<>();
885    // Add the spaces
886  128 EntityReference spaceReference = reference.extractReference(EntityType.SPACE);
887  128 EntityReference wikiReference = reference.extractReference(EntityType.WIKI);
888  128 for (EntityReference singleReference : spaceReference.removeParent(wikiReference).getReversedReferenceChain()) {
889  230 path.add(singleReference.getName());
890    }
891  128 if (reference.getType() == EntityType.DOCUMENT) {
892  107 path.add(reference.getName());
893    }
894  128 return path;
895    }
896   
897    /**
898    * @since 7.3M1
899    */
 
900  1007 toggle public String getCurrentWiki()
901    {
902  1007 return this.currentWiki;
903    }
904   
905    /**
906    * @since 7.3M1
907    */
 
908  13838 toggle public String getBaseURL()
909    {
910  13838 return XWikiExecutor.URL + ":"
911  13838 + (getCurrentExecutor() != null ? getCurrentExecutor().getPort() : XWikiExecutor.DEFAULT_PORT) + "/xwiki/";
912    }
913   
914    /**
915    * @since 7.3M1
916    */
 
917  1658 toggle public String getBaseBinURL()
918    {
919  1658 return getBaseURL() + "bin/";
920    }
921   
922    /**
923    * @since 7.2M1
924    */
 
925  1658 toggle public String getURL(String action, String[] path, String queryString)
926    {
927  1658 StringBuilder builder = new StringBuilder(getBaseBinURL());
928   
929  1658 if (!StringUtils.isEmpty(action)) {
930  1656 builder.append(action).append('/');
931    }
932  1658 List<String> escapedPath = new ArrayList<>();
933  1658 for (String element : path) {
934  3398 escapedPath.add(escapeURL(element));
935    }
936  1658 builder.append(StringUtils.join(escapedPath, '/'));
937   
938  1658 boolean needToAddSecretToken = !Arrays.asList("view", "register", "download").contains(action);
939  1658 if (needToAddSecretToken || !StringUtils.isEmpty(queryString)) {
940  828 builder.append('?');
941    }
942  1658 if (needToAddSecretToken) {
943  631 addQueryStringEntry(builder, "form_token", getSecretToken());
944  631 builder.append('&');
945    }
946  1658 if (!StringUtils.isEmpty(queryString)) {
947  747 builder.append(queryString);
948    }
949   
950  1658 return builder.toString();
951    }
952   
953    /**
954    * Get the URL of an action on a page with specified parameters. If you need to pass multiple parameters with the
955    * same key, this function will not work.
956    *
957    * @param space the space in which the page resides.
958    * @param page the name of the page.
959    * @param action the action to do on the page.
960    * @param queryParameters the parameters to pass in the URL, these will be automatically URL encoded.
961    */
 
962  173 toggle public String getURL(String space, String page, String action, Map<String, ?> queryParameters)
963    {
964  173 return getURL(space, page, action, toQueryString(queryParameters));
965    }
966   
967    /**
968    * @param space the name of the space that contains the page with the specified attachment
969    * @param page the name of the page that holds the attachment
970    * @param attachment the attachment name
971    * @param action the action to perform on the attachment
972    * @param queryString the URL query string
973    * @return the URL that performs the specified action on the specified attachment
974    */
 
975  1 toggle public String getAttachmentURL(String space, String page, String attachment, String action, String queryString)
976    {
977  1 return getURL(action, new String[] { space, page, attachment }, queryString);
978    }
979   
980    /**
981    * @param space the name of the space that contains the page with the specified attachment
982    * @param page the name of the page that holds the attachment
983    * @param attachment the attachment name
984    * @param action the action to perform on the attachment
985    * @return the URL that performs the specified action on the specified attachment
986    */
 
987  1 toggle public String getAttachmentURL(String space, String page, String attachment, String action)
988    {
989  1 return getAttachmentURL(space, page, attachment, action, "");
990    }
991   
992    /**
993    * @param space the name of the space that contains the page with the specified attachment
994    * @param page the name of the page that holds the attachment
995    * @param attachment the attachment name
996    * @return the URL to download the specified attachment
997    */
 
998  1 toggle public String getAttachmentURL(String space, String page, String attachment)
999    {
1000  1 return getAttachmentURL(space, page, attachment, "download");
1001    }
1002   
1003    /**
1004    * (Re)-cache the secret token used for CSRF protection. A user with edit rights on Main.WebHome must be logged in.
1005    * This method must be called before {@link #getSecretToken()} is called and after each re-login.
1006    *
1007    * @see #getSecretToken()
1008    */
 
1009  561 toggle public void recacheSecretToken()
1010    {
1011    // Save the current URL to be able to get back after we cache the secret token. We're not using the browser's
1012    // Back button because if the current page is the result of a POST request then by going back we are re-sending
1013    // the POST data which can have unexpected results. Moreover, some browsers pop up a modal confirmation box
1014    // which blocks the test.
1015  561 String previousURL = getDriver().getCurrentUrl();
1016    // Go to the registration page because the registration form uses secret token.
1017  561 gotoPage(getCurrentWiki(), "Register", "register");
1018  561 recacheSecretTokenWhenOnRegisterPage();
1019    // Return to the previous page.
1020  561 getDriver().get(previousURL);
1021    }
1022   
 
1023  614 toggle private void recacheSecretTokenWhenOnRegisterPage()
1024    {
1025  614 try {
1026  614 WebElement tokenInput = getDriver().findElement(By.xpath("//input[@name='form_token']"));
1027  614 this.secretToken = tokenInput.getAttribute("value");
1028    } catch (NoSuchElementException exception) {
1029    // Something is really wrong if this happens.
1030  0 System.out.println("Warning: Failed to cache anti-CSRF secret token, some tests might fail!");
1031  0 exception.printStackTrace();
1032    }
1033    }
1034   
1035    /**
1036    * Get the secret token used for CSRF protection. Remember to call {@link #recacheSecretToken()} first.
1037    *
1038    * @return anti-CSRF secret token, or empty string if the token was not cached
1039    * @see #recacheSecretToken()
1040    */
 
1041  1148 toggle public String getSecretToken()
1042    {
1043  1148 if (this.secretToken == null) {
1044  2 System.out.println("Warning: No cached anti-CSRF token found. "
1045    + "Make sure to call recacheSecretToken() before getSecretToken(), otherwise this test might fail.");
1046  2 return "";
1047    }
1048  1146 return this.secretToken;
1049    }
1050   
1051    /**
1052    * This class represents all cookies stored in the browser. Use with getSession() and setSession()
1053    */
 
1054    public class Session
1055    {
1056    private final Set<Cookie> cookies;
1057   
1058    private final String secretToken;
1059   
 
1060  32 toggle private Session(final Set<Cookie> cookies, final String secretToken)
1061    {
1062  32 this.cookies = Collections.unmodifiableSet(new HashSet<Cookie>()
1063    {
 
1064  32 toggle {
1065  32 addAll(cookies);
1066    }
1067    });
1068  32 this.secretToken = secretToken;
1069    }
1070   
 
1071  38 toggle private Set<Cookie> getCookies()
1072    {
1073  38 return this.cookies;
1074    }
1075   
 
1076  76 toggle private String getSecretToken()
1077    {
1078  76 return this.secretToken;
1079    }
1080    }
1081   
 
1082  0 toggle public boolean isInWYSIWYGEditMode()
1083    {
1084  0 return getDriver().findElements(By.xpath("//div[@id='editcolumn' and contains(@class, 'editor-wysiwyg')]"))
1085    .size() > 0;
1086    }
1087   
 
1088  1 toggle public boolean isInWikiEditMode()
1089    {
1090  1 return getDriver().findElements(By.xpath("//div[@id='editcolumn' and contains(@class, 'editor-wiki')]"))
1091    .size() > 0;
1092    }
1093   
 
1094  2 toggle public boolean isInViewMode()
1095    {
1096  2 return !getDriver().hasElementWithoutWaiting(By.id("editMeta"));
1097    }
1098   
 
1099  1 toggle public boolean isInSourceViewMode()
1100    {
1101  1 return getDriver().findElements(By.xpath("//textarea[@class = 'wiki-code']")).size() > 0;
1102    }
1103   
 
1104  1 toggle public boolean isInInlineEditMode()
1105    {
1106  1 String currentURL = getDriver().getCurrentUrl();
1107    // Keep checking the deprecated inline action for backward compatibility.
1108  1 return currentURL.contains("editor=inline") || currentURL.contains("/inline/");
1109    }
1110   
 
1111  1 toggle public boolean isInRightsEditMode()
1112    {
1113  1 return getDriver().getCurrentUrl().contains("editor=rights");
1114    }
1115   
 
1116  1 toggle public boolean isInObjectEditMode()
1117    {
1118  1 return getDriver().getCurrentUrl().contains("editor=object");
1119    }
1120   
 
1121  1 toggle public boolean isInClassEditMode()
1122    {
1123  1 return getDriver().getCurrentUrl().contains("editor=class");
1124    }
1125   
 
1126  1 toggle public boolean isInDeleteMode()
1127    {
1128  1 return getDriver().getCurrentUrl().contains("/delete/");
1129    }
1130   
 
1131  1 toggle public boolean isInRenameMode()
1132    {
1133  1 return getDriver().getCurrentUrl().contains("xpage=rename");
1134    }
1135   
 
1136  2 toggle public boolean isInCreateMode()
1137    {
1138  2 return getDriver().getCurrentUrl().contains("/create/");
1139    }
1140   
 
1141  1 toggle public boolean isInAdminMode()
1142    {
1143  1 return getDriver().getCurrentUrl().contains("/admin/");
1144    }
1145   
1146    /**
1147    * Forces the current user to be the Guest user by clearing all coookies.
1148    */
 
1149  68 toggle public void forceGuestUser()
1150    {
1151  68 setSession(null);
1152    }
1153   
 
1154  6 toggle public void addObject(String space, String page, String className, Object... properties)
1155    {
1156  6 gotoPage(space, page, "objectadd", toQueryParameters(className, null, properties));
1157    }
1158   
1159    /**
1160    * @since 7.2RC1
1161    */
 
1162  2 toggle public void addObject(EntityReference reference, String className, Object... properties)
1163    {
1164  2 gotoPage(reference, "objectadd", toQueryParameters(className, null, properties));
1165    }
1166   
1167    /**
1168    * @since 7.3M2
1169    */
 
1170  6 toggle public void addObject(EntityReference reference, String className, Map<String, ?> properties)
1171    {
1172  6 gotoPage(reference, "objectadd", toQueryParameters(className, null, properties));
1173    }
1174   
 
1175  0 toggle public void addObject(String space, String page, String className, Map<String, ?> properties)
1176    {
1177  0 gotoPage(space, page, "objectadd", toQueryParameters(className, null, properties));
1178    }
1179   
 
1180  0 toggle public void deleteObject(String space, String page, String className, int objectNumber) throws Exception
1181    {
1182  0 TestUtils.assertStatusCodes(
1183    rest().executeDelete(ObjectResource.class, getCurrentWiki(), space, page, className, objectNumber), true,
1184    STATUS_NO_CONTENT_NOT_FOUND);
1185    }
1186   
 
1187  0 toggle public void updateObject(String space, String page, String className, int objectNumber, Map<String, ?> properties)
1188    {
1189  0 gotoPage(space, page, "save", toQueryParameters(className, objectNumber, properties));
1190    }
1191   
 
1192  27 toggle public void updateObject(String space, String page, String className, int objectNumber, Object... properties)
1193    {
1194  27 updateObject(Collections.singletonList(space), page, className, objectNumber, properties);
1195    }
1196   
1197    /**
1198    * @since 8.3RC1
1199    */
 
1200  28 toggle public void updateObject(List<String> spaces, String page, String className, int objectNumber, Object... properties)
1201    {
1202    // TODO: would be even quicker using REST
1203  28 Map<String, Object> queryParameters =
1204    (Map<String, Object>) toQueryParameters(className, objectNumber, properties);
1205   
1206    // Append the updateOrCreate objectPolicy since we always want this in our tests.
1207  28 queryParameters.put("objectPolicy", "updateOrCreate");
1208   
1209  28 gotoPage(spaces, page, "save", queryParameters);
1210    }
1211   
 
1212  14 toggle public void addClassProperty(String space, String page, String propertyName, String propertyType)
1213    {
1214  14 gotoPage(space, page, "propadd", "propname", propertyName, "proptype", propertyType);
1215    }
1216   
1217    /**
1218    * @since 3.5M1
1219    */
 
1220  32 toggle public String toQueryString(Object... queryParameters)
1221    {
1222  32 return toQueryString(toQueryParameters(queryParameters));
1223    }
1224   
1225    /**
1226    * @since 3.5M1
1227    */
 
1228  414 toggle public String toQueryString(Map<String, ?> queryParameters)
1229    {
1230  414 StringBuilder builder = new StringBuilder();
1231   
1232  414 for (Map.Entry<String, ?> entry : queryParameters.entrySet()) {
1233  1308 addQueryStringEntry(builder, entry.getKey(), entry.getValue());
1234  1308 builder.append('&');
1235    }
1236   
1237  414 return builder.toString();
1238    }
1239   
1240    /**
1241    * @since 3.2M1
1242    */
 
1243  1308 toggle public void addQueryStringEntry(StringBuilder builder, String key, Object value)
1244    {
1245  1308 if (value != null) {
1246  1274 if (value instanceof Iterable) {
1247  0 for (Object element : (Iterable<?>) value) {
1248  0 addQueryStringEntry(builder, key, element.toString());
1249  0 builder.append('&');
1250    }
1251    } else {
1252  1274 addQueryStringEntry(builder, key, value.toString());
1253    }
1254    } else {
1255  34 addQueryStringEntry(builder, key, (String) null);
1256    }
1257    }
1258   
1259    /**
1260    * @since 3.2M1
1261    */
 
1262  1939 toggle public void addQueryStringEntry(StringBuilder builder, String key, String value)
1263    {
1264  1939 builder.append(escapeURL(key));
1265  1939 if (value != null) {
1266  1905 builder.append('=');
1267  1905 builder.append(escapeURL(value));
1268    }
1269    }
1270   
1271    /**
1272    * @since 3.5M1
1273    */
 
1274  32 toggle public Map<String, ?> toQueryParameters(Object... properties)
1275    {
1276  32 return toQueryParameters(null, null, properties);
1277    }
1278   
 
1279  68 toggle public Map<String, ?> toQueryParameters(String className, Integer objectNumber, Object... properties)
1280    {
1281  68 Map<String, Object> queryParameters = new HashMap<String, Object>();
1282   
1283  68 queryParameters.put("classname", className);
1284   
1285  184 for (int i = 0; i < properties.length; i += 2) {
1286  116 int nextIndex = i + 1;
1287  116 queryParameters.put(toQueryParameterKey(className, objectNumber, (String) properties[i]),
1288  116 nextIndex < properties.length ? properties[nextIndex] : null);
1289    }
1290   
1291  68 return queryParameters;
1292    }
1293   
 
1294  6 toggle public Map<String, ?> toQueryParameters(String className, Integer objectNumber, Map<String, ?> properties)
1295    {
1296  6 Map<String, Object> queryParameters = new HashMap<String, Object>();
1297   
1298  6 if (className != null) {
1299  6 queryParameters.put("classname", className);
1300    }
1301   
1302  6 for (Map.Entry<String, ?> entry : properties.entrySet()) {
1303  11 queryParameters.put(toQueryParameterKey(className, objectNumber, entry.getKey()), entry.getValue());
1304    }
1305   
1306  6 return queryParameters;
1307    }
1308   
 
1309  127 toggle public String toQueryParameterKey(String className, Integer objectNumber, String key)
1310    {
1311  127 if (className == null) {
1312  50 return key;
1313    } else {
1314  77 StringBuilder keyBuilder = new StringBuilder(className);
1315   
1316  77 keyBuilder.append('_');
1317   
1318  77 if (objectNumber != null) {
1319  43 keyBuilder.append(objectNumber);
1320  43 keyBuilder.append('_');
1321    }
1322   
1323  77 keyBuilder.append(key);
1324   
1325  77 return keyBuilder.toString();
1326    }
1327    }
1328   
 
1329  0 toggle public ObjectEditPage editObjects(String space, String page)
1330    {
1331  0 gotoPage(space, page, "edit", "editor=object");
1332  0 return new ObjectEditPage();
1333    }
1334   
 
1335  2 toggle public ClassEditPage editClass(String space, String page)
1336    {
1337  2 gotoPage(space, page, "edit", "editor=class");
1338  2 return new ClassEditPage();
1339    }
1340   
 
1341  0 toggle public String getVersion() throws Exception
1342    {
1343  0 Xwiki xwiki = rest().getResource("", null);
1344   
1345  0 return xwiki.getVersion();
1346    }
1347   
 
1348  0 toggle public String getMavenVersion() throws Exception
1349    {
1350  0 String version = getVersion();
1351   
1352  0 int index = version.indexOf('-');
1353  0 if (index > 0) {
1354  0 version = version.substring(0, index) + "-SNAPSHOT";
1355    }
1356   
1357  0 return version;
1358    }
1359   
 
1360  0 toggle public void attachFile(String space, String page, String name, File file, boolean failIfExists) throws Exception
1361    {
1362  0 InputStream is = new FileInputStream(file);
1363  0 try {
1364  0 attachFile(space, page, name, is, failIfExists);
1365    } finally {
1366  0 is.close();
1367    }
1368    }
1369   
1370    /**
1371    * @since 5.1M2
1372    */
 
1373  3 toggle public void attachFile(String space, String page, String name, InputStream is, boolean failIfExists,
1374    UsernamePasswordCredentials credentials) throws Exception
1375    {
1376  3 attachFile(Collections.singletonList(space), page, name, is, failIfExists, credentials);
1377    }
1378   
1379    /**
1380    * @since 7.2M2
1381    */
 
1382  4 toggle public void attachFile(List<String> spaces, String page, String name, InputStream is, boolean failIfExists,
1383    UsernamePasswordCredentials credentials) throws Exception
1384    {
1385  4 UsernamePasswordCredentials currentCredentials = getDefaultCredentials();
1386   
1387  4 try {
1388  4 if (credentials != null) {
1389  4 setDefaultCredentials(credentials);
1390    }
1391  4 attachFile(spaces, page, name, is, failIfExists);
1392    } finally {
1393  4 setDefaultCredentials(currentCredentials);
1394    }
1395    }
1396   
 
1397  3 toggle public void attachFile(String space, String page, String name, InputStream is, boolean failIfExists)
1398    throws Exception
1399    {
1400  3 attachFile(Collections.singletonList(space), page, name, is, failIfExists);
1401    }
1402   
1403    /**
1404    * @since 7.2M2
1405    */
 
1406  7 toggle public void attachFile(List<String> spaces, String page, String name, InputStream is, boolean failIfExists)
1407    throws Exception
1408    {
1409  7 AttachmentReference reference =
1410    new AttachmentReference(name, new DocumentReference(getCurrentWiki(), spaces, page));
1411   
1412  7 attachFile(reference, is, failIfExists);
1413    }
1414   
1415    /**
1416    * @since 7.3M1
1417    */
 
1418  19 toggle public void attachFile(EntityReference pageReference, String name, InputStream is, boolean failIfExists)
1419    throws Exception
1420    {
1421  19 EntityReference reference = new EntityReference(name, EntityType.ATTACHMENT, pageReference);
1422   
1423  19 attachFile(reference, is, failIfExists);
1424    }
1425   
1426    /**
1427    * @since 7.3M1
1428    */
 
1429  26 toggle public void attachFile(EntityReference reference, Object is, boolean failIfExists) throws Exception
1430    {
1431  26 rest().attachFile(reference, is, failIfExists);
1432    }
1433   
1434    // FIXME: improve that with a REST API to directly import a XAR
 
1435  0 toggle public void importXar(File file) throws Exception
1436    {
1437    // attach file
1438  0 attachFile("XWiki", "Import", file.getName(), file, false);
1439   
1440    // import file
1441  0 executeGet(
1442    getBaseBinURL() + "import/XWiki/Import?historyStrategy=add&importAsBackup=true&ajax&action=import&name="
1443    + escapeURL(file.getName()),
1444    Status.OK.getStatusCode());
1445    }
1446   
1447    /**
1448    * Delete the latest version from the history of a page, using the {@code /deleteversions/} action.
1449    *
1450    * @param space the space name of the page
1451    * @param page the name of the page
1452    * @since 7.0M2
1453    */
 
1454  2 toggle public void deleteLatestVersion(String space, String page)
1455    {
1456  2 deleteVersion(space, page, "latest");
1457    }
1458   
1459    /**
1460    * Delete a specific version from the history of a page, using the {@code /deleteversions/} action.
1461    *
1462    * @param space the space name of the page
1463    * @param page the name of the page
1464    * @param version the version to delete
1465    * @since 7.0M2
1466    */
 
1467  2 toggle public void deleteVersion(String space, String page, String version)
1468    {
1469  2 deleteVersions(space, page, version, version);
1470    }
1471   
1472    /**
1473    * Delete an interval of versions from the history of a page, using the {@code /deleteversions/} action.
1474    *
1475    * @param space the space name of the page
1476    * @param page the name of the page
1477    * @param v1 the starting version to delete
1478    * @param v2 the ending version to delete
1479    * @since 7.0M2
1480    */
 
1481  2 toggle public void deleteVersions(String space, String page, String v1, String v2)
1482    {
1483  2 gotoPage(space, page, "deleteversions", "rev1", v1, "rev2", v2, "confirm", "1");
1484    }
1485   
1486    /**
1487    * Roll back a page to the previous version, using the {@code /rollback/} action.
1488    *
1489    * @param space the space name of the page
1490    * @param page the name of the page
1491    * @since 7.0M2
1492    */
 
1493  0 toggle public void rollbackToPreviousVersion(String space, String page)
1494    {
1495  0 rollBackTo(space, page, "previous");
1496    }
1497   
1498    /**
1499    * Roll back a page to the specified version, using the {@code /rollback/} action.
1500    *
1501    * @param space the space name of the page
1502    * @param page the name of the page
1503    * @param version the version to rollback to
1504    * @since 7.0M2
1505    */
 
1506  0 toggle public void rollBackTo(String space, String page, String version)
1507    {
1508  0 gotoPage(space, page, "rollback", "rev", version, "confirm", "1");
1509    }
1510   
1511    /**
1512    * Set the hierarchy mode used in the wiki
1513    *
1514    * @param mode the mode to use ("reference" or "parentchild")
1515    * @since 7.2M2
1516    */
 
1517  12 toggle public void setHierarchyMode(String mode)
1518    {
1519  12 setPropertyInXWikiPreferences("core.hierarchyMode", "String", mode);
1520    }
1521   
1522    /**
1523    * Add and set a property into XWiki.XWikiPreferences. Create XWiki.XWikiPreferences if it does not exist.
1524    *
1525    * @param propertyName name of the property to set
1526    * @param propertyType the type of the property to add
1527    * @param value value to set to the property
1528    * @since 7.2M2
1529    */
 
1530  12 toggle public void setPropertyInXWikiPreferences(String propertyName, String propertyType, Object value)
1531    {
1532  12 addClassProperty("XWiki", "XWikiPreferences", propertyName, propertyType);
1533  12 gotoPage("XWiki", "XWikiPreferences", "edit", "editor", "object");
1534  12 ObjectEditPage objectEditPage = new ObjectEditPage();
1535  12 if (objectEditPage.hasObject("XWiki.XWikiPreferences")) {
1536  10 updateObject("XWiki", "XWikiPreferences", "XWiki.XWikiPreferences", 0, propertyName, value);
1537    } else {
1538  2 addObject("XWiki", "XWikiPreferences", "XWiki.XWikiPreferences", propertyName, value);
1539    }
1540    }
1541   
1542    /**
1543    * @since 7.3M1
1544    */
 
1545  11615 toggle public static void assertStatuses(int actualCode, int... expectedCodes)
1546    {
1547  11615 if (!ArrayUtils.contains(expectedCodes, actualCode)) {
1548  0 Assert.fail(
1549    "Unexpected code [" + actualCode + "], was expecting one of [" + Arrays.toString(expectedCodes) + "]");
1550    }
1551    }
1552   
1553    /**
1554    * @since 7.3M1
1555    */
 
1556  11615 toggle public static <M extends HttpMethod> M assertStatusCodes(M method, boolean release, int... expectedCodes)
1557    {
1558  11615 if (expectedCodes.length > 0) {
1559  11615 assertStatuses(method.getStatusCode(), expectedCodes);
1560    }
1561   
1562  11615 if (release) {
1563  549 method.releaseConnection();
1564    }
1565   
1566  11615 return method;
1567    }
1568   
1569    // HTTP
1570   
1571    /**
1572    * Encodes a given string so that it may be used as a URL component. Compatible with javascript decodeURIComponent,
1573    * though more strict than encodeURIComponent: all characters except [a-zA-Z0-9], '.', '-', '*', '_' are encoded.
1574    * Uses the same algorithm than the one used to generate URLs as otherwise tests won't find the proper matches...
1575    * See XWikiServletURLFactory#encodeWithinPath() and #encodeWithinQuery().
1576    *
1577    * @param url the url to encode
1578    */
 
1579  9070 toggle public String escapeURL(String url)
1580    {
1581  9070 String encodedURL;
1582  9070 try {
1583  9070 encodedURL = URLEncoder.encode(url, "UTF-8");
1584    } catch (Exception e) {
1585    // Should not happen (UTF-8 is always available)
1586  0 throw new RuntimeException("Missing charset [UTF-8]", e);
1587    }
1588   
1589    // The previous call will convert " " into "+" (and "+" into "%2B") so we need to convert "+" into "%20"
1590    // It's ok since %20 is allowed in both the URL path and the query string (and anchor).
1591  9070 encodedURL = encodedURL.replaceAll("\\+", "%20");
1592   
1593  9070 return encodedURL;
1594    }
1595   
1596    /**
1597    * Usage example:
1598    *
1599    * <pre>
1600    * {@code
1601    * By.xpath("//a[. = " + escapeXPath(value) + "]")
1602    * }
1603    * </pre>
1604    *
1605    * @param value the value to escape
1606    * @return the escaped value
1607    */
 
1608  1 toggle public String escapeXPath(String value)
1609    {
1610  1 return "concat('" + value.replace("'", "', \"'\", '") + "', '')";
1611    }
1612   
 
1613  0 toggle public InputStream getInputStream(String path, Map<String, ?> queryParams) throws Exception
1614    {
1615  0 return getInputStream(getBaseURL(), path, queryParams);
1616    }
1617   
 
1618  11029 toggle public String getString(String path, Map<String, ?> queryParams) throws Exception
1619    {
1620  11029 try (InputStream inputStream = getInputStream(getBaseURL(), path, queryParams)) {
1621  11029 return IOUtils.toString(inputStream);
1622    }
1623    }
1624   
 
1625  11061 toggle public InputStream getInputStream(String prefix, String path, Map<String, ?> queryParams, Object... elements)
1626    throws Exception
1627    {
1628  11061 String cleanPrefix = prefix.endsWith("/") ? prefix.substring(0, prefix.length() - 1) : prefix;
1629  11061 if (path.startsWith(cleanPrefix)) {
1630  0 cleanPrefix = "";
1631    }
1632   
1633  11061 UriBuilder builder = UriBuilder.fromUri(cleanPrefix).path(path.startsWith("/") ? path.substring(1) : path);
1634   
1635  11061 if (queryParams != null) {
1636  11049 for (Map.Entry<String, ?> entry : queryParams.entrySet()) {
1637  11048 if (entry.getValue() instanceof Object[]) {
1638  19 builder.queryParam(entry.getKey(), (Object[]) entry.getValue());
1639    } else {
1640  11029 builder.queryParam(entry.getKey(), entry.getValue());
1641    }
1642    }
1643    }
1644   
1645  11061 String url = builder.build(elements).toString();
1646   
1647  11061 return executeGet(url, Status.OK.getStatusCode()).getResponseBodyAsStream();
1648    }
1649   
 
1650  11217 toggle protected GetMethod executeGet(String uri) throws Exception
1651    {
1652  11217 GetMethod getMethod = new GetMethod(uri);
1653   
1654  11217 this.httpClient.executeMethod(getMethod);
1655   
1656  11217 return getMethod;
1657    }
1658   
 
1659  11061 toggle protected GetMethod executeGet(String uri, int... expectedCodes) throws Exception
1660    {
1661  11061 return executeGet(uri, false, expectedCodes);
1662    }
1663   
1664    /**
1665    * @since 7.3M1
1666    */
 
1667  11061 toggle protected GetMethod executeGet(String uri, boolean release, int... expectedCodes) throws Exception
1668    {
1669  11061 return assertStatusCodes(executeGet(uri), release, expectedCodes);
1670    }
1671   
1672    /**
1673    * @since 7.3M1
1674    */
 
1675  0 toggle protected PostMethod executePost(String uri, InputStream content, String mediaType) throws Exception
1676    {
1677  0 PostMethod postMethod = new PostMethod(uri);
1678  0 RequestEntity entity = new InputStreamRequestEntity(content, mediaType);
1679  0 postMethod.setRequestEntity(entity);
1680   
1681  0 this.httpClient.executeMethod(postMethod);
1682   
1683  0 return postMethod;
1684    }
1685   
 
1686  0 toggle protected PostMethod executePost(String uri, InputStream content, String mediaType, int... expectedCodes)
1687    throws Exception
1688    {
1689  0 return executePost(uri, content, mediaType, true, expectedCodes);
1690    }
1691   
1692    /**
1693    * @since 7.3M1
1694    */
 
1695  0 toggle protected PostMethod executePost(String uri, InputStream content, String mediaType, boolean release,
1696    int... expectedCodes) throws Exception
1697    {
1698  0 return assertStatusCodes(executePost(uri, content, mediaType), false, expectedCodes);
1699    }
1700   
1701    /**
1702    * @since 7.3M1
1703    */
 
1704  345 toggle protected DeleteMethod executeDelete(String uri) throws Exception
1705    {
1706  345 DeleteMethod postMethod = new DeleteMethod(uri);
1707   
1708  345 this.httpClient.executeMethod(postMethod);
1709   
1710  345 return postMethod;
1711    }
1712   
1713    /**
1714    * @since 7.3M1
1715    */
 
1716  0 toggle protected void executeDelete(String uri, int... expectedCodes) throws Exception
1717    {
1718  0 assertStatusCodes(executeDelete(uri), true, expectedCodes);
1719    }
1720   
1721    /**
1722    * @since 7.3M1
1723    */
 
1724  204 toggle protected PutMethod executePut(String uri, InputStream content, String mediaType) throws Exception
1725    {
1726  204 PutMethod putMethod = new PutMethod(uri);
1727  204 RequestEntity entity = new InputStreamRequestEntity(content, mediaType);
1728  204 putMethod.setRequestEntity(entity);
1729   
1730  204 this.httpClient.executeMethod(putMethod);
1731   
1732  204 return putMethod;
1733    }
1734   
 
1735  0 toggle protected void executePut(String uri, InputStream content, String mediaType, int... expectedCodes) throws Exception
1736    {
1737  0 executePut(uri, content, mediaType, true, expectedCodes);
1738    }
1739   
 
1740  0 toggle protected PutMethod executePut(String uri, InputStream content, String mediaType, boolean release,
1741    int... expectedCodes) throws Exception
1742    {
1743  0 return assertStatusCodes(executePut(uri, content, mediaType), release, expectedCodes);
1744    }
1745   
1746    // REST
1747   
 
1748  1519 toggle public RestTestUtils rest()
1749    {
1750  1519 return this.rest;
1751    }
1752   
1753    /**
1754    * @since 7.3M1
1755    */
1756    // TODO: Refactor TestUtils to move RestTestUtils tools to xwiki-platform-test-integration
 
1757    public static class RestTestUtils
1758    {
1759    public static final Boolean ELEMENTS_ENCODED = new Boolean(true);
1760   
1761    public static final Map<EntityType, ResourceAPI> RESOURCES_MAP = new IdentityHashMap<>();
1762   
 
1763    public static class ResourceAPI
1764    {
1765    public Class<?> api;
1766   
1767    public Class<?> localeAPI;
1768   
 
1769  132 toggle public ResourceAPI(Class<?> api, Class<?> localeAPI)
1770    {
1771  132 this.api = api;
1772  132 this.localeAPI = localeAPI;
1773    }
1774    }
1775   
1776    /**
1777    * Used to match number part of the object reference name.
1778    */
1779    private static final Pattern OBJECT_NAME_PATTERN = Pattern.compile("(\\\\*)\\[(\\d*)\\]$");
1780   
 
1781  33 toggle static {
1782  33 try {
1783    // Initialize REST related tools
1784  33 JAXBContext jaxbContext = JAXBContext
1785    .newInstance("org.xwiki.rest.model.jaxb:org.xwiki.extension.repository.xwiki.model.jaxb");
1786  33 marshaller = jaxbContext.createMarshaller();
1787  33 unmarshaller = jaxbContext.createUnmarshaller();
1788    } catch (JAXBException e) {
1789  0 throw new RuntimeException(e);
1790    }
1791   
1792  33 RESOURCES_MAP.put(EntityType.DOCUMENT, new ResourceAPI(PageResource.class, PageTranslationResource.class));
1793  33 RESOURCES_MAP.put(EntityType.OBJECT, new ResourceAPI(ObjectResource.class, null));
1794  33 RESOURCES_MAP.put(EntityType.OBJECT_PROPERTY, new ResourceAPI(ObjectPropertyResource.class, null));
1795  33 RESOURCES_MAP.put(EntityType.CLASS_PROPERTY, new ResourceAPI(ClassPropertyResource.class, null));
1796    }
1797   
1798    /**
1799    * @since 7.3M1
1800    */
 
1801  108 toggle public static org.xwiki.rest.model.jaxb.Object object(String className)
1802    {
1803  108 org.xwiki.rest.model.jaxb.Object obj = new org.xwiki.rest.model.jaxb.Object();
1804   
1805  108 obj.setClassName(className);
1806   
1807  108 return obj;
1808    }
1809   
1810    /**
1811    * @since 7.3M1
1812    */
 
1813  401 toggle public static String toPropertyString(Object value)
1814    {
1815  401 String stringValue;
1816   
1817  401 if (value instanceof Iterable) {
1818  60 StringBuilder builder = new StringBuilder();
1819  60 for (Object item : (Iterable) value) {
1820  4 if (builder.length() > 0) {
1821  1 builder.append('|');
1822    }
1823   
1824  4 builder.append(item);
1825    }
1826   
1827  60 stringValue = builder.toString();
1828  341 } else if (value != null) {
1829  314 stringValue = value.toString();
1830    } else {
1831  27 stringValue = null;
1832    }
1833   
1834  401 return stringValue;
1835    }
1836   
1837    /**
1838    * @since 7.3M1
1839    */
 
1840  401 toggle public static Property property(String name, Object value)
1841    {
1842  401 Property property = new Property();
1843   
1844  401 property.setName(name);
1845  401 property.setValue(toPropertyString(value));
1846   
1847  401 return property;
1848    }
1849   
1850    private TestUtils testUtils;
1851   
 
1852  117 toggle public RestTestUtils(TestUtils testUtils)
1853    {
1854  117 this.testUtils = testUtils;
1855    }
1856   
 
1857  1151 toggle public String getBaseURL()
1858    {
1859  1151 return this.testUtils.getBaseURL() + "rest";
1860    }
1861   
 
1862  175 toggle private String toSpaceElement(Iterable<?> spaces)
1863    {
1864  175 StringBuilder builder = new StringBuilder();
1865   
1866  175 for (Object space : spaces) {
1867  191 if (builder.length() > 0) {
1868  16 builder.append("/spaces/");
1869    }
1870   
1871  191 if (space instanceof EntityReference) {
1872  191 builder.append(((EntityReference) space).getName());
1873    } else {
1874  0 builder.append(space.toString());
1875    }
1876    }
1877   
1878  175 return builder.toString();
1879    }
1880   
 
1881  175 toggle private String toSpaceElement(String spaceReference)
1882    {
1883  175 return toSpaceElement(
1884    relativeReferenceResolver.resolve(spaceReference, EntityType.SPACE).getReversedReferenceChain());
1885    }
1886   
 
1887  175 toggle protected Object[] toElements(Page page)
1888    {
1889  175 List<Object> elements = new ArrayList<>();
1890   
1891    // Add wiki
1892  175 if (page.getWiki() != null) {
1893  171 elements.add(page.getWiki());
1894    } else {
1895  4 elements.add(this.testUtils.getCurrentWiki());
1896    }
1897   
1898    // Add spaces
1899  175 elements.add(toSpaceElement(page.getSpace()));
1900   
1901    // Add name
1902  175 elements.add(page.getName());
1903   
1904  175 return elements.toArray();
1905    }
1906   
 
1907  0 toggle public Object[] toElements(org.xwiki.rest.model.jaxb.Object obj, boolean onlyDocument)
1908    {
1909  0 List<Object> elements = new ArrayList<>();
1910   
1911    // Add wiki
1912  0 if (obj.getWiki() != null) {
1913  0 elements.add(obj.getWiki());
1914    } else {
1915  0 elements.add(this.testUtils.getCurrentWiki());
1916    }
1917   
1918    // Add spaces
1919  0 elements.add(toSpaceElement(obj.getSpace()));
1920   
1921    // Add name
1922  0 elements.add(obj.getPageName());
1923   
1924  0 if (!onlyDocument) {
1925    // Add class
1926  0 elements.add(obj.getClassName());
1927   
1928    // Add number
1929  0 elements.add(obj.getNumber());
1930    }
1931   
1932  0 return elements.toArray();
1933    }
1934   
 
1935  530 toggle public Object[] toElements(EntityReference reference)
1936    {
1937  530 List<EntityReference> references = reference.getReversedReferenceChain();
1938   
1939  530 List<Object> elements = new ArrayList<>(references.size() + 2);
1940   
1941    // Indicate that elements are already encoded
1942  530 elements.add(ELEMENTS_ENCODED);
1943   
1944    // Add current wiki if the reference does not contains any
1945  530 if (reference.extractReference(EntityType.WIKI) == null) {
1946  406 elements.add(this.testUtils.escapeURL(this.testUtils.getCurrentWiki()));
1947    }
1948   
1949    // Add reference
1950  530 for (EntityReference ref : references) {
1951  1399 if (ref.getType() == EntityType.SPACE) {
1952    // The URI builder does not support multiple elements like space reference so we hack it by doing
1953    // the opposite of what is done when reading the URL (generate a value looking like
1954    // "space1/spaces/space2")
1955  716 Object value = elements.get(elements.size() - 1);
1956   
1957  716 StringBuilder builder;
1958  716 if (value instanceof StringBuilder) {
1959  186 builder = (StringBuilder) value;
1960  186 builder.append("/spaces/");
1961    } else {
1962  530 builder = new StringBuilder();
1963  530 elements.add(builder);
1964    }
1965   
1966  716 builder.append(this.testUtils.escapeURL(ref.getName()));
1967  683 } else if (ref.getType() == EntityType.OBJECT) {
1968    // The REST API is no in sync with the ObjectReference structure:
1969    // was is a unique name in ObjectReference is two separated class name and index in REST API
1970  0 String classReferenceStr;
1971  0 String objectNumberStr;
1972   
1973  0 Matcher matcher = OBJECT_NAME_PATTERN.matcher(ref.getName());
1974  0 if (matcher.find()) {
1975  0 if (matcher.group(1).length() % 2 == 0) {
1976  0 classReferenceStr = ref.getName().substring(0, matcher.end(1));
1977  0 objectNumberStr = matcher.group(2);
1978    } else {
1979  0 classReferenceStr = ref.getName();
1980  0 objectNumberStr = null;
1981    }
1982    } else {
1983  0 classReferenceStr = ref.getName();
1984  0 objectNumberStr = null;
1985    }
1986   
1987  0 elements.add(classReferenceStr);
1988  0 elements.add(objectNumberStr);
1989    } else {
1990  683 elements.add(this.testUtils.escapeURL(ref.getName()));
1991    }
1992    }
1993   
1994    // Add locale
1995  530 Locale locale = getLocale(reference);
1996  530 if (locale != null) {
1997  6 elements.add(locale);
1998    }
1999   
2000  530 return elements.toArray();
2001    }
2002   
2003    /**
2004    * Add or update.
2005    */
 
2006  123 toggle public void save(Page page, int... expectedCodes) throws Exception
2007    {
2008  123 save(page, true, expectedCodes);
2009    }
2010   
 
2011  175 toggle public EntityEnclosingMethod save(Page page, boolean release, int... expectedCodes) throws Exception
2012    {
2013  175 if (expectedCodes.length == 0) {
2014    // Allow create or modify by default
2015  160 expectedCodes = STATUS_CREATED_ACCEPTED;
2016    }
2017   
2018  175 return TestUtils.assertStatusCodes(executePut(PageResource.class, page, toElements(page)), release,
2019    expectedCodes);
2020    }
2021   
2022    /**
2023    * @since 7.3M1
2024    */
 
2025  171 toggle public Page page(EntityReference reference)
2026    {
2027  171 Page page = new Page();
2028   
2029    // Add current wiki if the reference does not contains any
2030  171 EntityReference wikiReference = reference.extractReference(EntityType.WIKI);
2031  171 if (wikiReference == null) {
2032  28 page.setWiki(this.testUtils.getCurrentWiki());
2033    } else {
2034  143 page.setWiki(wikiReference.getName());
2035    }
2036   
2037    // Add spaces
2038  171 EntityReference spaceReference = reference.extractReference(EntityType.SPACE).removeParent(wikiReference);
2039  171 page.setSpace(referenceSerializer.serialize(spaceReference));
2040   
2041    // Add page
2042  171 EntityReference documentReference = reference.extractReference(EntityType.DOCUMENT);
2043  171 page.setName(documentReference.getName());
2044   
2045  171 return page;
2046    }
2047   
2048    /**
2049    * @since 7.3M1
2050    */
 
2051  36 toggle public void savePage(EntityReference reference) throws Exception
2052    {
2053  36 savePage(reference, null, null, null, null);
2054    }
2055   
2056    /**
2057    * @since 7.3M1
2058    */
 
2059  16 toggle public void savePage(EntityReference reference, String content, String title) throws Exception
2060    {
2061  16 savePage(reference, content, null, title, null);
2062    }
2063   
2064    /**
2065    * @since 7.3M1
2066    */
 
2067  52 toggle public void savePage(EntityReference reference, String content, String syntaxId, String title,
2068    String parentFullPageName) throws Exception
2069    {
2070  52 Page page = page(reference);
2071   
2072  52 if (content != null) {
2073  16 page.setContent(content);
2074    }
2075  52 if (title != null) {
2076  12 page.setTitle(title);
2077    }
2078  52 if (syntaxId != null) {
2079  0 page.setSyntax(syntaxId);
2080    }
2081  52 if (parentFullPageName != null) {
2082  0 page.setParent(parentFullPageName);
2083    }
2084   
2085  52 save(page, true);
2086    }
2087   
2088    /**
2089    * Add a new object.
2090    */
 
2091  0 toggle public void add(org.xwiki.rest.model.jaxb.Object obj) throws Exception
2092    {
2093  0 add(obj, true);
2094    }
2095   
2096    /**
2097    * Add a new object.
2098    */
 
2099  0 toggle public EntityEnclosingMethod add(org.xwiki.rest.model.jaxb.Object obj, boolean release) throws Exception
2100    {
2101  0 return TestUtils.assertStatusCodes(executePost(ObjectsResource.class, obj, toElements(obj, true)), release,
2102    STATUS_CREATED);
2103    }
2104   
2105    /**
2106    * Fail if the object does not exist.
2107    */
 
2108  0 toggle public void update(org.xwiki.rest.model.jaxb.Object obj) throws Exception
2109    {
2110  0 update(obj, true);
2111    }
2112   
2113    /**
2114    * Fail if the object does not exist.
2115    */
 
2116  0 toggle public EntityEnclosingMethod update(org.xwiki.rest.model.jaxb.Object obj, boolean release) throws Exception
2117    {
2118  0 return TestUtils.assertStatusCodes(executePut(ObjectResource.class, obj, toElements(obj, false)), release,
2119    STATUS_CREATED_ACCEPTED);
2120    }
2121   
 
2122  345 toggle public void delete(EntityReference reference) throws Exception
2123    {
2124  345 Class<?> resource = getResourceAPI(reference);
2125   
2126  345 TestUtils.assertStatusCodes(executeDelete(resource, toElements(reference)), true,
2127    STATUS_NO_CONTENT_NOT_FOUND);
2128    }
2129   
2130    // TODO: make EntityReference#getParameter() public
 
2131  1030 toggle private Locale getLocale(EntityReference reference)
2132    {
2133  1030 if (reference instanceof DocumentReference) {
2134  213 return ((DocumentReference) reference).getLocale();
2135  817 } else if (reference instanceof LocalDocumentReference) {
2136  774 return ((LocalDocumentReference) reference).getLocale();
2137    }
2138   
2139  43 return null;
2140    }
2141   
 
2142  149 toggle public void deletePage(String space, String page) throws Exception
2143    {
2144  149 delete(new LocalDocumentReference(space, page));
2145    }
2146   
2147    /**
2148    * @since 9.0RC1
2149    */
 
2150  0 toggle public void deletePage(String space, String page, Locale locale) throws Exception
2151    {
2152  0 delete(new LocalDocumentReference(space, page, locale));
2153    }
2154   
2155    /**
2156    * @since 8.0M1
2157    */
 
2158  29 toggle public void attachFile(EntityReference reference, Object is, boolean failIfExists) throws Exception
2159    {
2160    // make sure the page exist
2161  29 if (!exists(reference.getParent())) {
2162  2 savePage(reference.getParent());
2163    }
2164   
2165  29 if (failIfExists) {
2166  23 assertStatusCodes(executePut(AttachmentResource.class, is, toElements(reference)), true,
2167    STATUS_CREATED);
2168    } else {
2169  6 assertStatusCodes(executePut(AttachmentResource.class, is, toElements(reference)), true,
2170    STATUS_CREATED_ACCEPTED);
2171    }
2172    }
2173   
 
2174  151 toggle public boolean exists(EntityReference reference) throws Exception
2175    {
2176  151 GetMethod getMethod = executeGet(reference);
2177   
2178  151 getMethod.releaseConnection();
2179   
2180  151 return getMethod.getStatusCode() == Status.OK.getStatusCode();
2181    }
2182   
2183    /**
2184    * Return object model of the passed reference. Fail if none could be found.
2185    *
2186    * @since 7.3
2187    */
 
2188  4 toggle public <T> T get(EntityReference reference) throws Exception
2189    {
2190  4 return get(reference, true);
2191    }
2192   
2193    /**
2194    * Return object model of the passed reference or null if none could be found.
2195    *
2196    * @since 8.0M1
2197    */
 
2198  4 toggle public <T> T get(EntityReference reference, boolean failIfNotFound) throws Exception
2199    {
2200  4 Class<?> resource = getResourceAPI(reference);
2201   
2202  4 return get(resource, reference, failIfNotFound);
2203    }
2204   
2205    /**
2206    * @since 9.0RC1
2207    */
 
2208  500 toggle public Class<?> getResourceAPI(EntityReference reference) throws Exception
2209    {
2210  500 ResourceAPI resource = RESOURCES_MAP.get(reference.getType());
2211   
2212  500 if (resource == null) {
2213  0 throw new Exception("Unsuported type [" + reference.getType() + "]");
2214    }
2215   
2216  500 return getLocale(reference) != null ? resource.localeAPI : resource.api;
2217    }
2218   
2219    /**
2220    * Return object model of the passed reference with the passed resource URI. Fail if none could be found.
2221    *
2222    * @since 8.0M1
2223    */
 
2224  1 toggle public <T> T get(Object resourceURI, EntityReference reference) throws Exception
2225    {
2226  1 return get(resourceURI, reference, true);
2227    }
2228   
2229    /**
2230    * Return object model of the passed reference with the passed resource URI or null if none could be found.
2231    *
2232    * @since 8.0M1
2233    */
 
2234  5 toggle public <T> T get(Object resourceURI, EntityReference reference, boolean failIfNotFound) throws Exception
2235    {
2236  5 GetMethod getMethod = assertStatusCodes(executeGet(resourceURI, reference), false,
2237  5 failIfNotFound ? STATUS_OK : STATUS_OK_NOT_FOUND);
2238   
2239  5 if (getMethod.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
2240  0 return null;
2241    }
2242   
2243  5 if (reference.getType() == EntityType.ATTACHMENT) {
2244  0 return (T) getMethod.getResponseBodyAsStream();
2245    } else {
2246  5 try {
2247  5 try (InputStream stream = getMethod.getResponseBodyAsStream()) {
2248  5 return toResource(stream);
2249    }
2250    } finally {
2251  5 getMethod.releaseConnection();
2252    }
2253    }
2254    }
2255   
 
2256  32 toggle public InputStream getInputStream(String resourceUri, Map<String, ?> queryParams, Object... elements)
2257    throws Exception
2258    {
2259  32 return this.testUtils.getInputStream(getBaseURL(), resourceUri, queryParams, elements);
2260    }
2261   
 
2262  0 toggle public InputStream postRESTInputStream(Object resourceUri, Object restObject, Object... elements)
2263    throws Exception
2264    {
2265  0 return postInputStream(resourceUri, restObject, Collections.<String, Object[]>emptyMap(), elements);
2266    }
2267   
 
2268  0 toggle public InputStream postInputStream(Object resourceUri, Object restObject, Map<String, Object[]> queryParams,
2269    Object... elements) throws Exception
2270    {
2271  0 return executePost(resourceUri, restObject, queryParams, elements).getResponseBodyAsStream();
2272    }
2273   
 
2274  5 toggle public <T> T toResource(InputStream is) throws JAXBException
2275    {
2276  5 return (T) unmarshaller.unmarshal(is);
2277    }
2278   
 
2279  204 toggle protected InputStream toResourceInputStream(Object restObject) throws JAXBException
2280    {
2281  204 InputStream resourceStream;
2282  204 if (restObject instanceof InputStream) {
2283  28 resourceStream = (InputStream) restObject;
2284  176 } else if (restObject instanceof byte[]) {
2285  1 resourceStream = new ByteArrayInputStream((byte[]) restObject);
2286    } else {
2287  175 ByteArrayOutputStream stream = new ByteArrayOutputStream();
2288  175 marshaller.marshal(restObject, stream);
2289  175 resourceStream = new ByteArrayInputStream(stream.toByteArray());
2290    }
2291   
2292  204 return resourceStream;
2293    }
2294   
2295    /**
2296    * @since 7.3
2297    */
 
2298  151 toggle public GetMethod executeGet(EntityReference reference) throws Exception
2299    {
2300  151 Class<?> resource = getResourceAPI(reference);
2301   
2302  151 return executeGet(resource, reference);
2303    }
2304   
2305    /**
2306    * @since 8.0M1
2307    */
 
2308  156 toggle public GetMethod executeGet(Object resourceURI, EntityReference reference) throws Exception
2309    {
2310  156 return executeGet(resourceURI, toElements(reference));
2311    }
2312   
 
2313  156 toggle public GetMethod executeGet(Object resourceUri, Object... elements) throws Exception
2314    {
2315  156 return executeGet(resourceUri, Collections.<String, Object[]>emptyMap(), elements);
2316    }
2317   
 
2318  156 toggle public GetMethod executeGet(Object resourceUri, Map<String, Object[]> queryParams, Object... elements)
2319    throws Exception
2320    {
2321    // Build URI
2322  156 String uri = createUri(resourceUri, queryParams, elements).toString();
2323   
2324  156 return this.testUtils.executeGet(uri);
2325    }
2326   
 
2327  0 toggle public PostMethod executePost(Object resourceUri, Object restObject, Object... elements) throws Exception
2328    {
2329  0 return executePost(resourceUri, restObject, Collections.<String, Object[]>emptyMap(), elements);
2330    }
2331   
 
2332  0 toggle public PostMethod executePost(Object resourceUri, Object restObject, Map<String, Object[]> queryParams,
2333    Object... elements) throws Exception
2334    {
2335    // Build URI
2336  0 String uri = createUri(resourceUri, queryParams, elements).toString();
2337   
2338  0 try (InputStream resourceStream = toResourceInputStream(restObject)) {
2339  0 return this.testUtils.executePost(uri, resourceStream, MediaType.APPLICATION_XML);
2340    }
2341    }
2342   
 
2343  204 toggle public PutMethod executePut(Object resourceUri, Object restObject, Object... elements) throws Exception
2344    {
2345  204 return executePut(resourceUri, restObject, Collections.<String, Object[]>emptyMap(), elements);
2346    }
2347   
 
2348  204 toggle public PutMethod executePut(Object resourceUri, Object restObject, Map<String, Object[]> queryParams,
2349    Object... elements) throws Exception
2350    {
2351    // Build URI
2352  204 String uri = createUri(resourceUri, queryParams, elements).toString();
2353   
2354  204 try (InputStream resourceStream = toResourceInputStream(restObject)) {
2355  204 return this.testUtils.executePut(uri, resourceStream, MediaType.APPLICATION_XML);
2356    }
2357    }
2358   
 
2359  345 toggle public DeleteMethod executeDelete(Object resourceUri, Object... elements) throws Exception
2360    {
2361  345 return executeDelete(resourceUri, Collections.<String, Object[]>emptyMap(), elements);
2362    }
2363   
 
2364  345 toggle public DeleteMethod executeDelete(Object resourceUri, Map<String, Object[]> queryParams, Object... elements)
2365    throws Exception
2366    {
2367    // Build URI
2368  345 String uri = createUri(resourceUri, queryParams, elements).toString();
2369   
2370  345 return this.testUtils.executeDelete(uri);
2371    }
2372   
 
2373  937 toggle public URI createUri(Object resourceUri, Map<String, Object[]> queryParams, Object... elements)
2374    {
2375  937 if (resourceUri instanceof URI) {
2376  0 return (URI) resourceUri;
2377    }
2378   
2379    // Create URI builder
2380  937 UriBuilder builder = getUriBuilder(resourceUri, queryParams);
2381   
2382    // Build URI
2383  937 URI uri;
2384  937 if (elements.length > 0 && elements[0] == ELEMENTS_ENCODED) {
2385  530 uri = builder.buildFromEncoded(Arrays.copyOfRange(elements, 1, elements.length));
2386    } else {
2387  407 uri = builder.build(elements);
2388    }
2389   
2390  937 return uri;
2391    }
2392   
 
2393  937 toggle public UriBuilder getUriBuilder(Object resourceUri, Map<String, Object[]> queryParams)
2394    {
2395    // Create URI builder
2396  937 UriBuilder builder;
2397  937 if (resourceUri instanceof Class) {
2398  937 builder = getUriBuilder((Class) resourceUri);
2399    } else {
2400  0 String stringResourceUri = (String) resourceUri;
2401  0 builder = UriBuilder.fromUri(getBaseURL().substring(0, getBaseURL().length() - 1))
2402  0 .path(!stringResourceUri.isEmpty() && stringResourceUri.charAt(0) == '/'
2403    ? stringResourceUri.substring(1) : stringResourceUri);
2404    }
2405   
2406    // Add query parameters
2407  937 if (queryParams != null) {
2408  705 for (Map.Entry<String, Object[]> entry : queryParams.entrySet()) {
2409  0 builder.queryParam(entry.getKey(), entry.getValue());
2410    }
2411    }
2412   
2413  937 return builder;
2414    }
2415   
 
2416  937 toggle protected UriBuilder getUriBuilder(Class<?> resource)
2417    {
2418  937 return UriBuilder.fromUri(getBaseURL()).path(resource);
2419    }
2420   
 
2421  6 toggle public byte[] getBuffer(String resourceUri, Map<String, Object[]> queryParams, Object... elements)
2422    throws Exception
2423    {
2424  6 InputStream is = getInputStream(resourceUri, queryParams, elements);
2425   
2426  6 byte[] buffer;
2427  6 try {
2428  6 buffer = IOUtils.toByteArray(is);
2429    } finally {
2430  6 is.close();
2431    }
2432   
2433  6 return buffer;
2434    }
2435   
 
2436  26 toggle public <T> T getResource(String resourceUri, Map<String, Object[]> queryParams, Object... elements)
2437    throws Exception
2438    {
2439  26 T resource;
2440  26 try (InputStream is = getInputStream(resourceUri, queryParams, elements)) {
2441  26 resource = (T) unmarshaller.unmarshal(is);
2442    }
2443   
2444  26 return resource;
2445    }
2446    }
2447    }