001/*
002 * (C) Copyright 2011-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Sun Seng David TAN
018 *     Florent Guillaume
019 *     Benoit Delbosc
020 *     Antoine Taillefer
021 *     Anahide Tchertchian
022 *     Guillaume Renard
023 *     Mathieu Guillaume
024 *     Julien Carsique
025 */
026package org.nuxeo.functionaltests;
027
028import static org.junit.Assert.assertEquals;
029import static org.junit.Assert.assertTrue;
030import static org.nuxeo.functionaltests.Constants.ADMINISTRATOR;
031
032import java.io.File;
033import java.io.IOException;
034import java.lang.reflect.Constructor;
035import java.lang.reflect.Field;
036import java.net.MalformedURLException;
037import java.net.URL;
038import java.util.ArrayList;
039import java.util.List;
040import java.util.concurrent.TimeUnit;
041
042import org.apache.commons.io.FileUtils;
043import org.apache.commons.lang3.ArrayUtils;
044import org.apache.commons.logging.Log;
045import org.apache.commons.logging.LogFactory;
046import org.junit.After;
047import org.junit.AfterClass;
048import org.junit.BeforeClass;
049import org.junit.Rule;
050import org.junit.rules.MethodRule;
051import org.nuxeo.functionaltests.JavaScriptErrorCollector.JavaScriptErrorIgnoreRule;
052import org.nuxeo.functionaltests.drivers.ChromeDriverProvider;
053import org.nuxeo.functionaltests.drivers.FirefoxDriverProvider;
054import org.nuxeo.functionaltests.drivers.RemoteFirefoxDriverProvider;
055import org.nuxeo.functionaltests.fragment.WebFragment;
056import org.nuxeo.functionaltests.pages.AbstractPage;
057import org.nuxeo.functionaltests.pages.DocumentBasePage;
058import org.nuxeo.functionaltests.pages.DocumentBasePage.UserNotConnectedException;
059import org.nuxeo.functionaltests.pages.FileDocumentBasePage;
060import org.nuxeo.functionaltests.pages.LoginPage;
061import org.nuxeo.functionaltests.pages.NoteDocumentBasePage;
062import org.nuxeo.functionaltests.pages.tabs.CollectionContentTabSubPage;
063import org.nuxeo.functionaltests.proxy.ProxyManager;
064import org.nuxeo.runtime.api.Framework;
065import org.openqa.selenium.By;
066import org.openqa.selenium.NoSuchElementException;
067import org.openqa.selenium.NoSuchWindowException;
068import org.openqa.selenium.NotFoundException;
069import org.openqa.selenium.Proxy;
070import org.openqa.selenium.TimeoutException;
071import org.openqa.selenium.WebDriver;
072import org.openqa.selenium.WebElement;
073import org.openqa.selenium.internal.WrapsElement;
074import org.openqa.selenium.remote.CapabilityType;
075import org.openqa.selenium.remote.Command;
076import org.openqa.selenium.remote.DesiredCapabilities;
077import org.openqa.selenium.remote.DriverCommand;
078import org.openqa.selenium.remote.RemoteWebDriver;
079import org.openqa.selenium.support.PageFactory;
080import org.openqa.selenium.support.ui.FluentWait;
081import org.openqa.selenium.support.ui.Wait;
082
083import com.google.common.collect.ImmutableMap;
084
085/**
086 * Base functions for all pages.
087 */
088public abstract class AbstractTest {
089
090    /**
091     * @since 5.9.2
092     */
093    public final static String TEST_USERNAME = "jdoe";
094
095    /**
096     * @since 5.9.2
097     */
098    public final static String TEST_PASSWORD = "test";
099
100    /**
101     * Polling frequency in milliseconds.
102     *
103     * @since 5.9.2
104     */
105    public static final int POLLING_FREQUENCY_MILLISECONDS = 100;
106
107    public static final int POLLING_FREQUENCY_SECONDS = 1;
108
109    /**
110     * Page Load timeout in seconds.
111     *
112     * @since 5.9.2
113     */
114    public static final int PAGE_LOAD_TIME_OUT_SECONDS = 60;
115
116    public static final int LOAD_TIMEOUT_SECONDS = 30;
117
118    /**
119     * Driver implicit wait in milliseconds.
120     *
121     * @since 8.3
122     */
123    public static final int IMPLICIT_WAIT_MILLISECONDS = 200;
124
125    public static final int LOAD_SHORT_TIMEOUT_SECONDS = 2;
126
127    public static final int AJAX_TIMEOUT_SECONDS = 10;
128
129    public static final int AJAX_SHORT_TIMEOUT_SECONDS = 2;
130
131    /**
132     * @since 5.7
133     * @deprecated since 8.3
134     * @see ChromeDriverProvider
135     */
136    @Deprecated
137    public static final String CHROME_DRIVER_DEFAULT_PATH_LINUX = ChromeDriverProvider.CHROME_DRIVER_DEFAULT_PATH_LINUX;
138
139    /**
140     * @since 5.7 "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" doesn't work
141     * @deprecated since 8.3
142     * @see ChromeDriverProvider
143     */
144    @Deprecated
145    public static final String CHROME_DRIVER_DEFAULT_PATH_MAC = ChromeDriverProvider.CHROME_DRIVER_DEFAULT_PATH_MAC;
146
147    /**
148     * @since 5.7
149     * @deprecated since 8.3
150     * @see ChromeDriverProvider
151     */
152    @Deprecated
153    public static final String CHROME_DRIVER_DEFAULT_PATH_WINVISTA = ChromeDriverProvider.CHROME_DRIVER_DEFAULT_PATH_WINVISTA;
154
155    /**
156     * @since 5.7
157     * @deprecated since 8.3
158     * @see ChromeDriverProvider
159     */
160    @Deprecated
161    public static final String CHROME_DRIVER_DEFAULT_PATH_WINXP = ChromeDriverProvider.CHROME_DRIVER_DEFAULT_PATH_WINXP;
162
163    /**
164     * @since 5.7
165     * @deprecated since 8.3
166     * @see ChromeDriverProvider
167     */
168    @Deprecated
169    public static final String CHROME_DRIVER_DEFAULT_EXECUTABLE_NAME = ChromeDriverProvider.CHROME_DRIVER_DEFAULT_EXECUTABLE_NAME;
170
171    /**
172     * @since 5.7
173     * @deprecated since 8.3
174     * @see ChromeDriverProvider
175     */
176    @Deprecated
177    public static final String CHROME_DRIVER_WINDOWS_EXECUTABLE_NAME = ChromeDriverProvider.CHROME_DRIVER_WINDOWS_EXECUTABLE_NAME;
178
179    /**
180     * @deprecated since 8.3
181     * @see ChromeDriverProvider
182     */
183    @Deprecated
184    public static final String SYSPROP_CHROME_DRIVER_PATH = ChromeDriverProvider.SYSPROP_CHROME_DRIVER_PATH;
185
186    static final Log log = LogFactory.getLog(AbstractTest.class);
187
188    public static final String NUXEO_URL = System.getProperty("nuxeoURL", "http://localhost:8080/nuxeo")
189                                                 .replaceAll("/$", "");
190
191    public static RemoteWebDriver driver;
192
193    protected static ProxyManager proxyManager;
194
195    /**
196     * Logger method to follow what's being run on server logs and take a screenshot of the last page in case of failure
197     */
198    @Rule
199    public MethodRule watchman = new LogTestWatchman(driver, NUXEO_URL);
200
201    /**
202     * This method will be executed before any method registered with JUnit After annotation.
203     *
204     * @since 5.8
205     */
206    public void runBeforeAfters() {
207        ((LogTestWatchman) watchman).runBeforeAfters();
208    }
209
210    @BeforeClass
211    public static void initDriver() throws Exception {
212        String browser = System.getProperty("browser", "firefox");
213        // Use the same strings as command-line Selenium
214        if (browser.equals("chrome") || browser.equals("firefox")) {
215            initFirefoxDriver();
216        } else if (browser.equals("remotefirefox")) {
217            initRemoteFirefoxDriver();
218        } else if (browser.equals("googlechrome")) {
219            initChromeDriver();
220        } else {
221            throw new RuntimeException("Browser not supported: " + browser);
222        }
223        driver.manage().timeouts().pageLoadTimeout(PAGE_LOAD_TIME_OUT_SECONDS, TimeUnit.SECONDS);
224        driver.manage().timeouts().implicitlyWait(IMPLICIT_WAIT_MILLISECONDS, TimeUnit.MILLISECONDS);
225    }
226
227    protected static void initFirefoxDriver() throws Exception {
228        proxyManager = new ProxyManager();
229        Proxy proxy = proxyManager.startProxy();
230        if (proxy != null) {
231            proxy.setNoProxy("");
232        }
233        DesiredCapabilities dc = DesiredCapabilities.firefox();
234        dc.setCapability(CapabilityType.PROXY, proxy);
235        driver = new FirefoxDriverProvider().init(dc);
236    }
237
238    protected static void initRemoteFirefoxDriver() throws Exception {
239        proxyManager = new ProxyManager();
240        Proxy proxy = proxyManager.startProxy();
241        if (proxy != null) {
242            proxy.setNoProxy("");
243        }
244        DesiredCapabilities dc = DesiredCapabilities.firefox();
245        dc.setCapability(CapabilityType.PROXY, proxy);
246        driver = new RemoteFirefoxDriverProvider().init(dc);
247    }
248
249    protected static void initChromeDriver() throws Exception {
250        proxyManager = new ProxyManager();
251        Proxy proxy = proxyManager.startProxy();
252        DesiredCapabilities dc = DesiredCapabilities.chrome();
253        if (proxy != null) {
254            proxy.setNoProxy("");
255            dc.setCapability(CapabilityType.PROXY, proxy);
256        }
257        driver = new ChromeDriverProvider().init(dc);
258    }
259
260    /**
261     * @since 9.3
262     */
263    protected JavaScriptErrorIgnoreRule[] ignores = new JavaScriptErrorIgnoreRule[0];
264
265    /**
266     * @since 9.3
267     */
268    public void addAfterTestIgnores(JavaScriptErrorIgnoreRule... ignores) {
269        this.ignores = ArrayUtils.addAll(this.ignores, ignores);
270    }
271
272    /**
273     * @since 7.1
274     */
275    @After
276    public void checkJavascriptError() {
277        JavaScriptErrorCollector.from(driver).ignore(ignores).checkForErrors();
278    }
279
280    @AfterClass
281    public static void quitDriver() {
282        if (driver != null) {
283            driver.quit();
284            driver = null;
285        }
286
287        try {
288            proxyManager.stopProxy();
289            proxyManager = null;
290        } catch (Exception e) {
291            log.error("Could not stop proxy: " + e.getMessage());
292        }
293    }
294
295    public static <T> T get(String url, Class<T> pageClassToProxy, JavaScriptErrorIgnoreRule... ignores) {
296        JavaScriptErrorCollector.from(driver).ignore(ignores).checkForErrors();
297        driver.get(url);
298        return asPage(pageClassToProxy);
299    }
300
301    /**
302     * Opens given url adding hardcoded Seam conversation named "0NXMAIN".
303     *
304     * @since 8.3
305     */
306    public static void open(String url, JavaScriptErrorIgnoreRule... ignores) {
307        JavaScriptErrorCollector.from(driver).ignore(ignores).checkForErrors();
308        driver.get(NUXEO_URL + url + "?conversationId=0NXMAIN");
309    }
310
311    /**
312     * Do not wait for page load. Do not handle error. Do not give explicit error in case of failure. This is a very raw
313     * get.
314     *
315     * @since 6.0
316     */
317    public static <T> T getWithoutErrorHandler(String url, Class<T> pageClassToProxy) throws IOException {
318        Command command = new Command(AbstractTest.driver.getSessionId(), DriverCommand.GET,
319                ImmutableMap.of("url", url));
320        AbstractTest.driver.getCommandExecutor().execute(command);
321        return asPage(pageClassToProxy);
322    }
323
324    public static WebDriver getPopup() {
325        return switchToPopup(null);
326    }
327
328    /**
329     * Focus popup that contains text parameter value in his current URL.
330     *
331     * @param text that must be contained in popup URL
332     * @return WebDriver instance to be chained
333     * @since 9.3
334     */
335    public static WebDriver switchToPopup(String text) {
336        String currentWindow = null;
337        try {
338            currentWindow = driver.getWindowHandle();
339        } catch (NoSuchWindowException ignored) {
340            // Nothing to do; it can happen when manipulating closed popups
341        }
342
343        for (String popup : driver.getWindowHandles()) {
344            if (popup.equals(currentWindow)) {
345                continue;
346            }
347
348            driver.switchTo().window(popup);
349            if (text == null || driver.getCurrentUrl().contains(text)) {
350                return driver;
351            }
352        }
353
354        throw new NotFoundException("Popup not found: " + text);
355    }
356
357    public static <T> T asPage(Class<T> pageClassToProxy) {
358        T page = instantiatePage(pageClassToProxy);
359        return fillElement(pageClassToProxy, page);
360    }
361
362    public static <T extends WebFragment> T getWebFragment(By by, Class<T> webFragmentClass) {
363        WebElement element = Locator.findElementWithTimeout(by);
364        return getWebFragment(element, webFragmentClass);
365    }
366
367    public static <T extends WebFragment> T getWebFragment(WebElement element, Class<T> webFragmentClass) {
368        T webFragment = instantiateWebFragment(element, webFragmentClass);
369        webFragment = fillElement(webFragmentClass, webFragment);
370        // fillElement somehow overwrite the 'element' field, reset it.
371        webFragment.setElement(element);
372        return webFragment;
373    }
374
375    /**
376     * Fills an instantiated page/form/widget attributes
377     *
378     * @since 5.7
379     */
380    public static <T> T fillElement(Class<T> pageClassToProxy, T page) {
381        PageFactory.initElements(new VariableElementLocatorFactory(driver, AJAX_TIMEOUT_SECONDS), page);
382        // check all required WebElements on the page and wait for their
383        // loading
384        final List<String> fieldNames = new ArrayList<>();
385        final List<WrapsElement> elements = new ArrayList<>();
386        for (Field field : pageClassToProxy.getDeclaredFields()) {
387            if (field.getAnnotation(Required.class) != null) {
388                try {
389                    field.setAccessible(true);
390                    fieldNames.add(field.getName());
391                    elements.add((WrapsElement) field.get(page));
392                } catch (Exception e) {
393                    throw new RuntimeException(e);
394                }
395            }
396        }
397
398        Wait<T> wait = new FluentWait<>(page).withTimeout(LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
399                                             .pollingEvery(POLLING_FREQUENCY_MILLISECONDS, TimeUnit.MILLISECONDS);
400        T res;
401        try {
402            res = wait.until(aPage -> {
403                String notLoaded = anyElementNotLoaded(elements, fieldNames);
404                if (notLoaded == null) {
405                    return aPage;
406                } else {
407                    return null;
408                }
409            });
410        } catch (TimeoutException e) {
411            throw new TimeoutException("not loaded: " + anyElementNotLoaded(elements, fieldNames), e);
412        }
413        // check if there are JQuery ajax requests to complete
414        if (pageClassToProxy.isAnnotationPresent(WaitForJQueryAjaxOnLoading.class)) {
415            new AjaxRequestManager(driver).waitForJQueryRequests();
416        }
417        return res;
418    }
419
420    protected static String anyElementNotLoaded(List<WrapsElement> proxies, List<String> fieldNames) {
421        for (int i = 0; i < proxies.size(); i++) {
422            WrapsElement proxy = proxies.get(i);
423            try {
424                // method implemented in LocatingElementHandler
425                proxy.getWrappedElement();
426            } catch (NoSuchElementException e) {
427                return fieldNames.get(i);
428            }
429        }
430        return null;
431    }
432
433    // private in PageFactory...
434    protected static <T> T instantiatePage(Class<T> pageClassToProxy) {
435        try {
436            try {
437                Constructor<T> constructor = pageClassToProxy.getConstructor(WebDriver.class);
438                return constructor.newInstance(driver);
439            } catch (NoSuchMethodException e) {
440                return pageClassToProxy.newInstance();
441            }
442        } catch (RuntimeException e) {
443            throw e;
444        } catch (Exception e) {
445            throw new RuntimeException(e);
446        }
447    }
448
449    protected static <T extends WebFragment> T instantiateWebFragment(WebElement element, Class<T> webFragmentClass) {
450        try {
451            try {
452                Constructor<T> constructor = webFragmentClass.getConstructor(WebDriver.class, WebElement.class);
453                return constructor.newInstance(driver, element);
454            } catch (NoSuchMethodException e) {
455                return webFragmentClass.newInstance();
456            }
457        } catch (RuntimeException e) {
458            throw e;
459        } catch (Exception e) {
460            throw new RuntimeException(e);
461        }
462    }
463
464    public LoginPage getLoginPage() {
465        return get(NUXEO_URL + "/logout", LoginPage.class);
466    }
467
468    public LoginPage logout() {
469        JavaScriptErrorCollector.from(driver).ignore(ignores).checkForErrors();
470        return getLoginPage();
471    }
472
473    /**
474     * Logs out without expecting to be redirected to the login page. This can be the case on a simple server
475     * distribution when logged in: the logout action can redirect to the home.html startup page.
476     *
477     * @since 9.2
478     */
479    public void logoutSimply() {
480        driver.get(NUXEO_URL + "/logout");
481    }
482
483    /**
484     * navigate to a link text. wait until the link is available and click on it.
485     */
486    public <T extends AbstractPage> T nav(Class<T> pageClass, String linkText) {
487        WebElement link = Locator.findElementWithTimeout(By.linkText(linkText));
488        if (link == null) {
489            return null;
490        }
491        link.click();
492        return asPage(pageClass);
493    }
494
495    /**
496     * Navigate to a specified url
497     *
498     * @param urlString url
499     * @throws MalformedURLException
500     */
501    public void navToUrl(String urlString) throws MalformedURLException {
502        URL url = new URL(urlString);
503        driver.navigate().to(url);
504    }
505
506    /**
507     * Login as Administrator
508     *
509     * @return the Document base page (by default returned by CAP)
510     * @throws UserNotConnectedException
511     */
512    public DocumentBasePage login() throws UserNotConnectedException {
513        return login(ADMINISTRATOR, ADMINISTRATOR);
514    }
515
516    public DocumentBasePage login(String username, String password) throws UserNotConnectedException {
517        DocumentBasePage documentBasePage = getLoginPage().login(username, password, DocumentBasePage.class);
518        documentBasePage.checkUserConnected(username);
519        return documentBasePage;
520    }
521
522    /**
523     * Login as default test user.
524     *
525     * @since 5.9.2
526     */
527    public DocumentBasePage loginAsTestUser() throws UserNotConnectedException {
528        return login(TEST_USERNAME, TEST_PASSWORD);
529    }
530
531    /**
532     * Login using an invalid credential.
533     *
534     * @param username the username
535     * @param password the password
536     */
537    public LoginPage loginInvalid(String username, String password) {
538        return getLoginPage().login(username, password, LoginPage.class);
539    }
540
541    /**
542     * Init the repository with a test Workspace form the {@code currentPage}.
543     *
544     * @param currentPage the current page
545     * @return the created Workspace page
546     * @throws Exception if initializing repository fails
547     * @deprecated since 8.3
548     */
549    @Deprecated
550    protected DocumentBasePage initRepository(DocumentBasePage currentPage) throws Exception {
551        return createWorkspace(currentPage, "Test Workspace", "Test Workspace for my dear WebDriver.");
552    }
553
554    /**
555     * Cleans the repository (delete the test Workspace) from the {@code currentPage}.
556     *
557     * @param currentPage the current page
558     * @throws Exception if cleaning repository fails
559     * @deprecated since 8.3
560     */
561    @Deprecated
562    protected void cleanRepository(DocumentBasePage currentPage) throws Exception {
563        deleteWorkspace(currentPage, "Test Workspace");
564    }
565
566    /**
567     * Creates a Workspace from the {@code currentPage}.
568     *
569     * @param currentPage the current page
570     * @param workspaceTitle the workspace title
571     * @param workspaceDescription the workspace description
572     * @return the created Workspace page
573     * @deprecated since 8.3: use {@link DocumentBasePage#createWorkspace(String, String)} instead.
574     */
575    @Deprecated
576    protected DocumentBasePage createWorkspace(DocumentBasePage currentPage, String workspaceTitle,
577            String workspaceDescription) {
578        return currentPage.createWorkspace(workspaceTitle, workspaceDescription);
579    }
580
581    /**
582     * Deletes the Workspace with title {@code workspaceTitle} from the {@code currentPage}.
583     *
584     * @param currentPage the current page
585     * @param workspaceTitle the workspace title
586     * @deprecated since 8.3: use {@link DocumentBasePage#deleteWorkspace(String)} instead.
587     */
588    @Deprecated
589    protected void deleteWorkspace(DocumentBasePage currentPage, String workspaceTitle) {
590        currentPage.deleteWorkspace(workspaceTitle);
591    }
592
593    /**
594     * Creates a File form the {@code currentPage}.
595     *
596     * @param currentPage the current page
597     * @param fileTitle the file title
598     * @param fileDescription the file description
599     * @param uploadBlob true if a blob needs to be uploaded (temporary file created for this purpose)
600     * @param filePrefix the file prefix
601     * @param fileSuffix the file suffix
602     * @param fileContent the file content
603     * @return the created File page
604     * @throws IOException if temporary file creation fails
605     * @deprecated since 8.3: use {@link DocumentBasePage#createFile(String, String, boolean, String, String, String)}
606     *             instead.
607     */
608    @Deprecated
609    protected FileDocumentBasePage createFile(DocumentBasePage currentPage, String fileTitle, String fileDescription,
610            boolean uploadBlob, String filePrefix, String fileSuffix, String fileContent) throws IOException {
611        return currentPage.createFile(fileTitle, fileDescription, uploadBlob, filePrefix, fileSuffix, fileContent);
612    }
613
614    /**
615     * Creates a Collections container form the {@code currentPage}.
616     *
617     * @param currentPage the current page
618     * @param collectionsTitle the Collections container title
619     * @param fileDescription the collections description
620     * @return the created Collections page
621     * @deprecated since 8.3: use {@link DocumentBasePage#createCollections(String, String)} instead.
622     */
623    @Deprecated
624    protected DocumentBasePage createCollections(DocumentBasePage currentPage, String collectionsTitle,
625            String fileDescription) {
626        return currentPage.createCollections(collectionsTitle, fileDescription);
627    }
628
629    /**
630     * Creates a Collection form the {@code currentPage}.
631     *
632     * @param currentPage the current page
633     * @param collectionsTitle the Collections container title
634     * @param fileDescription the collection description
635     * @return the created Collections page
636     * @deprecated since 8.3: use {@link DocumentBasePage#createCollection(String, String)} instead.
637     */
638    @Deprecated
639    protected CollectionContentTabSubPage createCollection(DocumentBasePage currentPage, String collectionsTitle,
640            String fileDescription) {
641        return currentPage.createCollection(collectionsTitle, fileDescription);
642    }
643
644    /**
645     * Creates a temporary file and returns its absolute path.
646     *
647     * @param filePrefix the file prefix
648     * @param fileSuffix the file suffix
649     * @param fileContent the file content
650     * @return the temporary file to upload path
651     * @throws IOException if temporary file creation fails
652     * @since 5.9.3
653     */
654    public static String getTmpFileToUploadPath(String filePrefix, String fileSuffix, String fileContent)
655            throws IOException {
656        // Create tmp file, deleted on exit
657        File tmpFile = Framework.createTempFile(filePrefix, fileSuffix);
658        tmpFile.deleteOnExit();
659        FileUtils.writeStringToFile(tmpFile, fileContent);
660        assertTrue(tmpFile.exists());
661
662        // Check file URI protocol
663        assertEquals("file", tmpFile.toURI().toURL().getProtocol());
664
665        // Return file absolute path
666        return tmpFile.getAbsolutePath();
667    }
668
669    /**
670     * Get the current document id stored in the javascript ctx.currentDocument variable of the current page.
671     *
672     * @return the current document id
673     * @since 5.7
674     */
675    protected String getCurrentDocumentId() {
676        return (String) driver.executeScript("return ctx.currentDocument;");
677    }
678
679    /**
680     * Creates a Note form the {@code currentPage}.
681     *
682     * @param currentPage the current page
683     * @param noteTitle the note title
684     * @param noteDescription the note description
685     * @param defineNote true if the content of the note needs to be defined
686     * @param noteContent the content of the note
687     * @return the created note page.
688     * @throws IOException
689     * @since 5.9.4
690     * @deprecated since 8.3: use {@link DocumentBasePage#createNote(String, String, boolean, String)} instead.
691     */
692    @Deprecated
693    protected NoteDocumentBasePage createNote(DocumentBasePage currentPage, String noteTitle, String noteDescription,
694            boolean defineNote, String noteContent) throws IOException {
695        return currentPage.createNote(noteTitle, noteDescription, defineNote, noteContent);
696    }
697
698}