Project

General

Profile

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

    
3
import static org.junit.Assert.assertNull;
4

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

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

    
29
import com.google.common.base.Function;
30

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

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

    
46
    /**
47
     *
48
     */
49
    public static final int WAIT_SECONDS = 25;
50

    
51
    public static final Logger logger = Logger.getLogger(PortalPage.class);
52

    
53
    protected final static String DRUPAL_PAGE_QUERY_BASE = "?q=";
54

    
55
    protected WebDriver driver;
56

    
57
    protected DataPortalContext context;
58

    
59
    protected final JUnitWebDriverWait wait;
60

    
61
    public WebDriverWait getWait() {
62
        return wait;
63
    }
64

    
65

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

    
75
    private String drupalPagePath;
76

    
77
    protected URL pageUrl;
78

    
79
    // ==== WebElements === //
80

    
81
    @FindBy(className="node")
82
    protected WebElement portalContent;
83

    
84
//    @FindBy(tagName="title")
85
//    @CacheLookup
86
//    protected WebElement title;
87

    
88
    @FindBy(className="node")
89
    protected WebElement node;
90

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

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

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

    
103
    @FindBy(className="messages_error")
104
    @CacheLookup
105
    protected WebElement messagesError;
106

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

    
125
        this.driver = driver;
126

    
127
        this.context = context;
128

    
129
        this.wait = new JUnitWebDriverWait(driver, WAIT_SECONDS);
130

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

    
133
        this.pageUrl = new URL(context.getBaseUri().toString() + DRUPAL_PAGE_QUERY_BASE + drupalPagePath);
134

    
135
        // tell browser to navigate to the page
136
        driver.get(pageUrl.toString());
137

    
138
        takeScreenShot();
139

    
140
        // This call sets the WebElement fields.
141
        PageFactory.initElements(driver, this);
142

    
143

    
144
        try {
145
            assertNull("The page must not show an error box", messagesError.getText());
146
        } catch (NoSuchElementException e) {
147
            //IGNORE since this is expected!
148
        }
149

    
150
        logger.info("loading " + pageUrl);
151

    
152
    }
153

    
154
    /**
155
     * Creates a new PortaPage at given URL location. An Exception is thrown if
156
     * this URL is not matching the expected URL for the specific page type.
157
     *
158
     * @param driver
159
     * @param context
160
     * @param url
161
     * @throws Exception
162
     */
163
    public PortalPage(WebDriver driver, DataPortalContext context, URL url) throws Exception {
164

    
165
        this.driver = driver;
166

    
167
        this.context = context;
168

    
169
        this.wait = new JUnitWebDriverWait(driver, 25);
170

    
171
        this.pageUrl = new URL(context.getBaseUri().toString());
172

    
173
        // tell browser to navigate to the given URL
174
        driver.get(url.toString());
175

    
176
        takeScreenShot();
177

    
178
        if(!isOnPage()){
179
            throw new Exception("Not on the expected portal page ( current: " + driver.getCurrentUrl() + ", expected: " +  pageUrl + " )");
180
        }
181

    
182
        this.pageUrl = url;
183

    
184
        logger.info("loading " + pageUrl);
185

    
186
        // This call sets the WebElement fields.
187
        PageFactory.initElements(driver, this);
188

    
189
    }
190

    
191
    /**
192
     * Creates a new PortaPage at the WebDrivers current URL location. An Exception is thrown if
193
     * driver.getCurrentUrl() is not matching the expected URL for the specific page type.
194
     *
195
     * @param driver
196
     * @param context
197
     * @throws Exception
198
     */
199
    public PortalPage(WebDriver driver, DataPortalContext context) throws Exception {
200

    
201
        this.driver = driver;
202

    
203
        this.context = context;
204

    
205
        this.wait = new JUnitWebDriverWait(driver, 25);
206

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

    
211
        takeScreenShot();
212

    
213
        if(!isOnPage()){
214
            throw new Exception("Not on the expected portal page ( current: " + driver.getCurrentUrl() + ", expected: " +  pageUrl + " )");
215
        }
216

    
217
        // now set the real URL
218
        this.pageUrl = new URL(driver.getCurrentUrl());
219

    
220
        logger.info("loading " + pageUrl);
221

    
222
        // This call sets the WebElement fields.
223
        PageFactory.initElements(driver, this);
224

    
225
    }
226

    
227
    /**
228
     * @return
229
     */
230
    protected boolean isOnPage() {
231
        return driver.getCurrentUrl().startsWith(pageUrl.toString());
232
    }
233

    
234
    /**
235
     * navigate and reload the page if not jet there
236
     */
237
    public void get() {
238
        if(!driver.getCurrentUrl().equals(pageUrl.toString())){
239
            driver.get(pageUrl.toString());
240
            wait.until(new UrlLoaded(pageUrl.toString()));
241
            // take screenshot of new page
242
            takeScreenShot();
243
            PageFactory.initElements(driver, this);
244
        }
245
    }
246

    
247
    /**
248
     * go back in history
249
     */
250
    public void back() {
251
        driver.navigate().back();
252
    }
253

    
254
    public String getDrupalPagePath() {
255
        return drupalPagePath;
256
    }
257

    
258
    /**
259
     *
260
     * @return the page title
261
     * @deprecated use {@link driver#getTitle()}
262
     */
263
    @Deprecated
264
    public String getTitle() {
265
        return driver.getTitle();
266
    }
267

    
268
    /**
269
     * returns the warning messages from the Drupal message box
270
     * @return
271
     */
272
    public String getWarnings() {
273
        return null; //TODO unimplemented
274
    }
275

    
276
    /**
277
     * returns the error messages from the Drupal message box
278
     * @return
279
     */
280
    public String getErrors() {
281
        return null; //TODO unimplemented
282
    }
283

    
284
    public String getAuthorInformationText() {
285

    
286
        WebElement authorInformation = null;
287

    
288
        try {
289
            authorInformation  = node.findElement(By.className("submitted"));
290
        } catch (NoSuchElementException e) {
291
            // IGNORE //
292
        }
293

    
294

    
295
        if(authorInformation != null){
296
            return authorInformation.getText();
297
        } else {
298
            return null;
299
        }
300
    }
301

    
302
    public List<LinkElement> getPrimaryTabs(){
303
        List<LinkElement> tabs = new ArrayList<LinkElement>();
304
        List<WebElement> links = primaryTabs.findElements(By.tagName("a"));
305
        for(WebElement a : links) {
306
            WebElement renderedLink = a;
307
            if(renderedLink.isDisplayed()){
308
                tabs.add(new LinkElement(renderedLink));
309
            }
310
        }
311

    
312
        return tabs;
313
    }
314

    
315
    public ClassificationTreeBlock getClassificationTree() {
316
        return new ClassificationTreeBlock(classificationBrowserBlock);
317
    }
318

    
319
    public void hover(WebElement element) {
320
        Actions actions = new Actions(driver);
321
        actions.moveToElement(element, 1, 1).perform();
322
        logger.debug("hovering");
323
    }
324

    
325

    
326
    /**
327
     * Returns the current URL string from the {@link WebDriver}
328
     * @return
329
     */
330
    public URL getPageURL() {
331
        return pageUrl;
332
    }
333

    
334

    
335
    /**
336
     * return the <code>scheme://domain:port</code> part of the initial url of this page.
337
     * @return
338
     */
339
    public String getInitialUrlBase() {
340
        return pageUrl.getProtocol() + "://" + pageUrl.getHost() + pageUrl.getPort();
341
    }
342

    
343
    @Override
344
    public boolean equals(Object obj) {
345
        if (PortalPage.class.isAssignableFrom(obj.getClass())) {
346
            PortalPage page = (PortalPage) obj;
347
            return this.getPageURL().toString().equals(page.getPageURL().toString());
348

    
349
        } else {
350
            return false;
351
        }
352
    }
353

    
354

    
355
    /**
356
     * @param <T>
357
     * @param link the link to click
358
     * @param isTrue see {@link org.openqa.selenium.support.ui.FluentWait#until(Function)}
359
     * @param pageType the return type
360
     * @param duration may be null, if this in null <code>waitUnit</code> will be ignored.
361
     * @param waitUnit may be null, is ignored if <code>duration</code> is null defaults to {@link TimeUnit.SECONDS}
362
     * @return
363
     * @throws SecurityException
364
     */
365
    public <T extends PortalPage> T clickLink(BaseElement element, Function<? super WebDriver, Boolean> isTrue, Class<T> pageType, Long duration, TimeUnit waitUnit) {
366

    
367
        if( pageType.getClass().equals(PortalPage.class) ) {
368
            throw new RuntimeException("Parameter pageType must be a subclass of PortalPage");
369
        }
370
        String targetWindow = null;
371
        List<String> targets = element.getLinkTargets(driver);
372
        if(targets.size() > 0){
373
            targetWindow = targets.get(0);
374
        }
375

    
376
        if(logger.isInfoEnabled() || logger.isDebugEnabled()){
377
            logger.info("clickLink() on " + element.toStringWithLinks());
378
        }
379
        element.getElement().click();
380
        if(targetWindow != null){
381
            driver.switchTo().window(targetWindow);
382
        }
383

    
384
        try {
385
            if(duration != null){
386
                if(waitUnit == null){
387
                    waitUnit = TimeUnit.SECONDS;
388
                }
389
                wait.withTimeout(duration, waitUnit).until(isTrue);
390
            } else {
391
                wait.until(isTrue);
392
            }
393
        } catch (AssertionError timeout){
394
            logger.info("clickLink timed out. Current WindowHandles:" + driver.getWindowHandles());
395
            throw timeout;
396
        }
397

    
398

    
399
        Constructor<T> constructor;
400
        T pageInstance;
401
        try {
402
            constructor = pageType.getConstructor(WebDriver.class, DataPortalContext.class);
403
            pageInstance = constructor.newInstance(driver, context);
404
        } catch (Exception e) {
405
            throw new RuntimeException(e);
406
        }
407
        // take screenshot of new page
408
        takeScreenShot();
409
        return pageInstance;
410
    }
411

    
412

    
413
    /**
414
     * @param <T>
415
     * @param link the link to click
416
     * @param isTrue see {@link org.openqa.selenium.support.ui.FluentWait#until(Function)}
417
     * @param type the return type
418
     * @return
419
     */
420
    public <T extends PortalPage> T clickLink(BaseElement element, Function<? super WebDriver, Boolean> isTrue, Class<T> type) {
421
        return clickLink(element, isTrue, type, null, null);
422
    }
423

    
424

    
425
    /**
426
     * replaces all underscores '_' by hyphens '-'
427
     *
428
     * @param featureName
429
     * @return
430
     */
431
    protected String normalizeClassAttribute(String featureName) {
432
        return featureName.replace('_', '-');
433
    }
434

    
435
    public File takeScreenShot(){
436

    
437

    
438
        logger.info("Screenshot ...");
439
        File destFile = fileForTestMethod(new File("screenshots"));
440
        if(logger.isDebugEnabled()){
441
            logger.debug("Screenshot destFile" + destFile.getPath().toString());
442
        }
443
        File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
444
        try {
445
            FileUtils.copyFile(scrFile, destFile);
446
            logger.info("Screenshot taken and saved as " + destFile.getAbsolutePath());
447
            return destFile;
448
        } catch (IOException e) {
449
            logger.error("could not copy sceenshot to " + destFile.getAbsolutePath(), e);
450
        }
451

    
452
        return null;
453

    
454
    }
455

    
456
    /**
457
     * Finds the test class and method in the stack trace which is using the
458
     * PortalPage and returns a File object consisting of:
459
     * {@code $targetFolder/$className/$methodName }
460
     *
461
     *
462
     * If no test class is found it will fall back to using
463
     * "noTest" as folder name and a timestamp as filename.
464
     * @return
465
     */
466
    private File fileForTestMethod(File targetFolder){
467

    
468
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
469
        for (StackTraceElement stackTraceElement : trace) {
470
            // according to the convention all test class names should end with "Test"
471
            if(logger.isTraceEnabled()){
472
                logger.trace("fileForTestMethod() - " + stackTraceElement.toString());
473
            }
474
            if(stackTraceElement.getClassName().endsWith("Test")){
475
                return uniqueIndexedFile(
476
                            targetFolder.getAbsolutePath() + File.separator + stackTraceElement.getClassName(),
477
                            stackTraceElement.getMethodName(),
478
                            "png");
479

    
480
            }
481
        }
482
        return uniqueIndexedFile(
483
                targetFolder.getAbsolutePath() + File.separator + "noTest",
484
                Long.toString(System.currentTimeMillis()),
485
                "png");
486
    }
487

    
488

    
489
    private File uniqueIndexedFile(String folder, String fileName, String suffix){
490
        File file;
491
        int i = 0;
492
        while(true){
493
            file = new File(folder + File.separator + fileName + "_" + Integer.toString(i++) + "." + suffix);
494
            if(!file.exists()){
495
                return file;
496
            }
497
        }
498
    }
499

    
500

    
501
}
(3-3/6)