Project

General

Profile

Download (14.5 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.dataportal.pages;
2

    
3
import java.io.File;
4
import java.io.IOException;
5
import java.lang.reflect.Constructor;
6
import java.net.MalformedURLException;
7
import java.net.URL;
8
import java.util.ArrayList;
9
import java.util.List;
10
import java.util.concurrent.TimeUnit;
11

    
12
import org.apache.commons.io.FileUtils;
13
import org.apache.log4j.Logger;
14
import org.openqa.selenium.By;
15
import org.openqa.selenium.NoSuchElementException;
16
import org.openqa.selenium.OutputType;
17
import org.openqa.selenium.TakesScreenshot;
18
import org.openqa.selenium.WebDriver;
19
import org.openqa.selenium.WebElement;
20
import org.openqa.selenium.interactions.Actions;
21
import org.openqa.selenium.support.CacheLookup;
22
import org.openqa.selenium.support.FindBy;
23
import org.openqa.selenium.support.FindBys;
24
import org.openqa.selenium.support.PageFactory;
25
import org.openqa.selenium.support.ui.WebDriverWait;
26

    
27
import com.google.common.base.Function;
28

    
29
import eu.etaxonomy.dataportal.DataPortalContext;
30
import eu.etaxonomy.dataportal.elements.BaseElement;
31
import eu.etaxonomy.dataportal.elements.ClassificationTreeBlock;
32
import eu.etaxonomy.dataportal.elements.LinkElement;
33
import eu.etaxonomy.dataportal.selenium.JUnitWebDriverWait;
34
import eu.etaxonomy.dataportal.selenium.UrlLoaded;
35

    
36
/**
37
 * FIXME only works with the cichorieae theme
38
 *
39
 * @author a.kohlbecker
40
 *
41
 */
42
public abstract class  PortalPage {
43

    
44
    /**
45
     *
46
     */
47
    public static final int WAIT_SECONDS = 25;
48

    
49
    public static final Logger logger = Logger.getLogger(PortalPage.class);
50

    
51
    protected final static String DRUPAL_PAGE_QUERY_BASE = "?q=";
52

    
53
    protected WebDriver driver;
54

    
55
    protected DataPortalContext context;
56

    
57
    protected final JUnitWebDriverWait wait;
58

    
59
    public WebDriverWait getWait() {
60
        return wait;
61
    }
62

    
63

    
64
    /**
65
     * Implementations of this method will supply the relative
66
     * path to the Drupal page. This path will usally have the form
67
     * <code>cdm_dataportal/{nodetype}</code>. For example the taxon pages all
68
     * have the page base <code>cdm_dataportal/taxon</code>
69
     * @return
70
     */
71
    protected abstract String getDrupalPageBase();
72

    
73
    private String drupalPagePath;
74

    
75
    protected URL pageUrl;
76

    
77
    // ==== WebElements === //
78

    
79
    @FindBy(className="node")
80
    protected WebElement portalContent;
81

    
82
//    @FindBy(tagName="title")
83
//    @CacheLookup
84
//    protected WebElement title;
85

    
86
    @FindBy(className="node")
87
    protected WebElement node;
88

    
89
    @FindBys({@FindBy(id="tabs-wrapper"), @FindBy(className="primary")})
90
    @CacheLookup
91
    protected WebElement primaryTabs;
92

    
93
    @FindBy(id="block-cdm-dataportal-2")
94
    @CacheLookup
95
    protected WebElement searchBlockElement;
96

    
97
    @FindBy(id="block-cdm-taxontree-cdm-tree")
98
    @CacheLookup
99
    protected WebElement classificationBrowserBlock;
100

    
101
    /**
102
     * Creates a new PortaPage. Implementations of this class will provide the base path of the page by
103
     * implementing the method {@link #getDrupalPageBase()}. The constructor argument <code>pagePathSuffix</code>
104
     * specifies the specific page to navigate to. For example:
105
     * <ol>
106
     * <li>{@link #getDrupalPageBase()} returns <code>/cdm_dataportal/taxon</code></li>
107
     * <li><code>pagePathSuffix</code> gives <code>7fe8a8b6-b0ba-4869-90b3-177b76c1753f</code></li>
108
     * </ol>
109
     * Both are combined to form the URL pathelement <code>/cdm_dataportal/taxon/7fe8a8b6-b0ba-4869-90b3-177b76c1753f</code>
110
     *
111
     *
112
     * @param driver
113
     * @param context
114
     * @param pagePathSuffix
115
     * @throws MalformedURLException
116
     */
117
    public PortalPage(WebDriver driver, DataPortalContext context, String pagePathSuffix) throws MalformedURLException {
118

    
119
        this.driver = driver;
120

    
121
        this.context = context;
122

    
123
        this.wait = new JUnitWebDriverWait(driver, WAIT_SECONDS);
124

    
125
        this.drupalPagePath = getDrupalPageBase() + (pagePathSuffix != null ? "/" + pagePathSuffix: "");
126

    
127
        this.pageUrl = new URL(context.getBaseUri().toString() + DRUPAL_PAGE_QUERY_BASE + drupalPagePath);
128

    
129
        // tell browser to navigate to the page
130
        driver.get(pageUrl.toString());
131

    
132
        takeScreenShot();
133

    
134
        // This call sets the WebElement fields.
135
        PageFactory.initElements(driver, this);
136

    
137
        logger.info("loading " + pageUrl);
138

    
139
    }
140

    
141
    /**
142
     * Creates a new PortaPage at given URL location. An Exception is thrown if
143
     * this URL is not matching the expected URL for the specific page type.
144
     *
145
     * @param driver
146
     * @param context
147
     * @param url
148
     * @throws Exception
149
     */
150
    public PortalPage(WebDriver driver, DataPortalContext context, URL url) throws Exception {
151

    
152
        this.driver = driver;
153

    
154
        this.context = context;
155

    
156
        this.wait = new JUnitWebDriverWait(driver, 25);
157

    
158
        this.pageUrl = new URL(context.getBaseUri().toString());
159

    
160
        // tell browser to navigate to the given URL
161
        driver.get(url.toString());
162

    
163
        takeScreenShot();
164

    
165
        if(!isOnPage()){
166
            throw new Exception("Not on the expected portal page ( current: " + driver.getCurrentUrl() + ", expected: " +  pageUrl + " )");
167
        }
168

    
169
        this.pageUrl = url;
170

    
171
        logger.info("loading " + pageUrl);
172

    
173
        // This call sets the WebElement fields.
174
        PageFactory.initElements(driver, this);
175

    
176
    }
177

    
178
    /**
179
     * Creates a new PortaPage at the WebDrivers current URL location. An Exception is thrown if
180
     * driver.getCurrentUrl() is not matching the expected URL for the specific page type.
181
     *
182
     * @param driver
183
     * @param context
184
     * @throws Exception
185
     */
186
    public PortalPage(WebDriver driver, DataPortalContext context) throws Exception {
187

    
188
        this.driver = driver;
189

    
190
        this.context = context;
191

    
192
        this.wait = new JUnitWebDriverWait(driver, 25);
193

    
194
        // preliminary set the pageUrl to the base path of this page, this is used in the next setp to check if the
195
        // driver.getCurrentUrl() is a sub path of the base path
196
        this.pageUrl = new URL(context.getBaseUri().toString());
197

    
198
        takeScreenShot();
199

    
200
        if(!isOnPage()){
201
            throw new Exception("Not on the expected portal page ( current: " + driver.getCurrentUrl() + ", expected: " +  pageUrl + " )");
202
        }
203

    
204
        // now set the real URL
205
        this.pageUrl = new URL(driver.getCurrentUrl());
206

    
207
        logger.info("loading " + pageUrl);
208

    
209
        // This call sets the WebElement fields.
210
        PageFactory.initElements(driver, this);
211

    
212
    }
213

    
214
    /**
215
     * @return
216
     */
217
    protected boolean isOnPage() {
218
        return driver.getCurrentUrl().startsWith(pageUrl.toString());
219
    }
220

    
221
    /**
222
     * navigate and reload the page if not jet there
223
     */
224
    public void get() {
225
        if(!driver.getCurrentUrl().equals(pageUrl.toString())){
226
            driver.get(pageUrl.toString());
227
            wait.until(new UrlLoaded(pageUrl.toString()));
228
            // take screenshot of new page
229
            takeScreenShot();
230
            PageFactory.initElements(driver, this);
231
        }
232
    }
233

    
234
    /**
235
     * go back in history
236
     */
237
    public void back() {
238
        driver.navigate().back();
239
    }
240

    
241
    public String getDrupalPagePath() {
242
        return drupalPagePath;
243
    }
244

    
245
    /**
246
     *
247
     * @return the page title
248
     * @deprecated use {@link driver#getTitle()}
249
     */
250
    @Deprecated
251
    public String getTitle() {
252
        return driver.getTitle();
253
    }
254

    
255
    /**
256
     * returns the warning messages from the Drupal message box
257
     * @return
258
     */
259
    public String getWarnings() {
260
        return null; //TODO unimplemented
261
    }
262

    
263
    /**
264
     * returns the error messages from the Drupal message box
265
     * @return
266
     */
267
    public String getErrors() {
268
        return null; //TODO unimplemented
269
    }
270

    
271
    public String getAuthorInformationText() {
272

    
273
        WebElement authorInformation = null;
274

    
275
        try {
276
            authorInformation  = node.findElement(By.className("submitted"));
277
        } catch (NoSuchElementException e) {
278
            // IGNORE //
279
        }
280

    
281

    
282
        if(authorInformation != null){
283
            return authorInformation.getText();
284
        } else {
285
            return null;
286
        }
287
    }
288

    
289
    public List<LinkElement> getPrimaryTabs(){
290
        List<LinkElement> tabs = new ArrayList<LinkElement>();
291
        List<WebElement> links = primaryTabs.findElements(By.tagName("a"));
292
        for(WebElement a : links) {
293
            WebElement renderedLink = a;
294
            if(renderedLink.isDisplayed()){
295
                tabs.add(new LinkElement(renderedLink));
296
            }
297
        }
298

    
299
        return tabs;
300
    }
301

    
302
    public ClassificationTreeBlock getClassificationTree() {
303
        return new ClassificationTreeBlock(classificationBrowserBlock);
304
    }
305

    
306
    public void hover(WebElement element) {
307
        Actions actions = new Actions(driver);
308
        actions.moveToElement(element, 1, 1).perform();
309
        logger.debug("hovering");
310
    }
311

    
312

    
313
    /**
314
     * Returns the current URL string from the {@link WebDriver}
315
     * @return
316
     */
317
    public URL getPageURL() {
318
        return pageUrl;
319
    }
320

    
321

    
322
    /**
323
     * return the <code>scheme://domain:port</code> part of the initial url of this page.
324
     * @return
325
     */
326
    public String getInitialUrlBase() {
327
        return pageUrl.getProtocol() + "://" + pageUrl.getHost() + pageUrl.getPort();
328
    }
329

    
330
    @Override
331
    public boolean equals(Object obj) {
332
        if (PortalPage.class.isAssignableFrom(obj.getClass())) {
333
            PortalPage page = (PortalPage) obj;
334
            return this.getPageURL().toString().equals(page.getPageURL().toString());
335

    
336
        } else {
337
            return false;
338
        }
339
    }
340

    
341

    
342
    /**
343
     * @param <T>
344
     * @param link the link to click
345
     * @param isTrue see {@link org.openqa.selenium.support.ui.FluentWait#until(Function)}
346
     * @param pageType the return type
347
     * @param duration may be null, if this in null <code>waitUnit</code> will be ignored.
348
     * @param waitUnit may be null, is ignored if <code>duration</code> is null defaults to {@link TimeUnit.SECONDS}
349
     * @return
350
     * @throws SecurityException
351
     */
352
    public <T extends PortalPage> T clickLink(BaseElement element, Function<? super WebDriver, Boolean> isTrue, Class<T> pageType, Long duration, TimeUnit waitUnit) {
353

    
354
        if( pageType.getClass().equals(PortalPage.class) ) {
355
            throw new RuntimeException("Parameter pageType must be a subclass of PortalPage");
356
        }
357
        String targetWindow = null;
358
        List<String> targets = element.getLinkTargets(driver);
359
        if(targets.size() > 0){
360
            targetWindow = targets.get(0);
361
        }
362

    
363
        if(logger.isInfoEnabled() || logger.isDebugEnabled()){
364
            logger.info("clickLink() on " + element.toStringWithLinks());
365
        }
366
        element.getElement().click();
367
        if(targetWindow != null){
368
            driver.switchTo().window(targetWindow);
369
        }
370

    
371
        try {
372
            if(duration != null){
373
                if(waitUnit == null){
374
                    waitUnit = TimeUnit.SECONDS;
375
                }
376
                wait.withTimeout(duration, waitUnit).until(isTrue);
377
            } else {
378
                wait.until(isTrue);
379
            }
380
        } catch (AssertionError timeout){
381
            logger.info("clickLink timed out. Current WindowHandles:" + driver.getWindowHandles());
382
            throw timeout;
383
        }
384

    
385

    
386
        Constructor<T> constructor;
387
        T pageInstance;
388
        try {
389
            constructor = pageType.getConstructor(WebDriver.class, DataPortalContext.class);
390
            pageInstance = constructor.newInstance(driver, context);
391
        } catch (Exception e) {
392
            throw new RuntimeException(e);
393
        }
394
        // take screenshot of new page
395
        takeScreenShot();
396
        return pageInstance;
397
    }
398

    
399

    
400
    /**
401
     * @param <T>
402
     * @param link the link to click
403
     * @param isTrue see {@link org.openqa.selenium.support.ui.FluentWait#until(Function)}
404
     * @param type the return type
405
     * @return
406
     */
407
    public <T extends PortalPage> T clickLink(BaseElement element, Function<? super WebDriver, Boolean> isTrue, Class<T> type) {
408
        return clickLink(element, isTrue, type, null, null);
409
    }
410

    
411

    
412
    /**
413
     * replaces all underscores '_' by hyphens '-'
414
     *
415
     * @param featureName
416
     * @return
417
     */
418
    protected String normalizeClassAttribute(String featureName) {
419
        return featureName.replace('_', '-');
420
    }
421

    
422
    public File takeScreenShot(){
423

    
424

    
425
        logger.info("Screenshot ...");
426
        File destFile = fileForTestMethod(new File("screenshots"));
427
        if(logger.isDebugEnabled()){
428
            logger.debug("Screenshot destFile" + destFile.getPath().toString());
429
        }
430
        File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
431
        try {
432
            FileUtils.copyFile(scrFile, destFile);
433
            logger.info("Screenshot taken and saved as " + destFile.getAbsolutePath());
434
            return destFile;
435
        } catch (IOException e) {
436
            logger.error("could not copy sceenshot to " + destFile.getAbsolutePath(), e);
437
        }
438

    
439
        return null;
440

    
441
    }
442

    
443
    /**
444
     * Finds the test class and method in the stack trace which is using the
445
     * PortalPage and returns a File object consisting of:
446
     * {@code $targetFolder/$className/$methodName }
447
     *
448
     *
449
     * If no test class is found it will fall back to using
450
     * "noTest" as folder name and a timestamp as filename.
451
     * @return
452
     */
453
    private File fileForTestMethod(File targetFolder){
454

    
455
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
456
        for (StackTraceElement stackTraceElement : trace) {
457
            // according to the convention all test class names should end with "Test"
458
            if(logger.isTraceEnabled()){
459
                logger.trace("fileForTestMethod() - " + stackTraceElement.toString());
460
            }
461
            if(stackTraceElement.getClassName().endsWith("Test")){
462
                return uniqueIndexedFile(
463
                            targetFolder.getAbsolutePath() + File.separator + stackTraceElement.getClassName(),
464
                            stackTraceElement.getMethodName(),
465
                            "png");
466

    
467
            }
468
        }
469
        return uniqueIndexedFile(
470
                targetFolder.getAbsolutePath() + File.separator + "noTest",
471
                Long.toString(System.currentTimeMillis()),
472
                "png");
473
    }
474

    
475

    
476
    private File uniqueIndexedFile(String folder, String fileName, String suffix){
477
        File file;
478
        int i = 0;
479
        while(true){
480
            file = new File(folder + File.separator + fileName + "_" + Integer.toString(i++) + "." + suffix);
481
            if(!file.exists()){
482
                return file;
483
            }
484
        }
485
    }
486

    
487

    
488
}
(3-3/6)