Project

General

Profile

Download (14.1 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
public abstract class  PortalPage {
37

    
38
    /**
39
     *
40
     */
41
    public static final int WAIT_SECONDS = 25;
42

    
43
    public static final Logger logger = Logger.getLogger(PortalPage.class);
44

    
45
    protected final static String DRUPAL_PAGE_QUERY_BASE = "?q=";
46

    
47
    protected WebDriver driver;
48

    
49
    protected DataPortalContext context;
50

    
51
    protected final JUnitWebDriverWait wait;
52

    
53
    public WebDriverWait getWait() {
54
        return wait;
55
    }
56

    
57

    
58
    /**
59
     * Implementations of this method will supply the relative
60
     * path to the Drupal page. This path will usally have the form
61
     * <code>cdm_dataportal/{nodetype}</code>. For example the taxon pages all
62
     * have the page base <code>cdm_dataportal/taxon</code>
63
     * @return
64
     */
65
    protected abstract String getDrupalPageBase();
66

    
67
    private String drupalPagePath;
68

    
69
    protected URL pageUrl;
70

    
71
    // ==== WebElements === //
72

    
73
    @FindBy(className="node")
74
    protected WebElement portalContent;
75

    
76
//    @FindBy(tagName="title")
77
//    @CacheLookup
78
//    protected WebElement title;
79

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

    
83
    @FindBys({@FindBy(id="tabs-wrapper"), @FindBy(className="primary")})
84
    @CacheLookup
85
    protected WebElement primaryTabs;
86

    
87
    @FindBy(id="block-cdm-dataportal-2")
88
    @CacheLookup
89
    protected WebElement searchBlockElement;
90

    
91
    @FindBy(id="block-cdm-taxontree-cdm-tree")
92
    @CacheLookup
93
    protected WebElement classificationBrowserBlock;
94

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

    
113
        this.driver = driver;
114

    
115
        this.context = context;
116

    
117
        this.wait = new JUnitWebDriverWait(driver, WAIT_SECONDS);
118

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

    
121
        this.pageUrl = new URL(context.getBaseUri().toString() + DRUPAL_PAGE_QUERY_BASE + drupalPagePath);
122

    
123
        // tell browser to navigate to the page
124
        driver.get(pageUrl.toString());
125

    
126
        takeScreenShot();
127

    
128
        // This call sets the WebElement fields.
129
        PageFactory.initElements(driver, this);
130

    
131
        logger.info("loading " + pageUrl);
132

    
133
    }
134

    
135
    /**
136
     * Creates a new PortaPage at given URL location. An Exception is thrown if
137
     * this URL is not matching the expected URL for the specific page type.
138
     *
139
     * @param driver
140
     * @param context
141
     * @param url
142
     * @throws Exception
143
     */
144
    public PortalPage(WebDriver driver, DataPortalContext context, URL url) throws Exception {
145

    
146
        this.driver = driver;
147

    
148
        this.context = context;
149

    
150
        this.wait = new JUnitWebDriverWait(driver, 25);
151

    
152
        this.pageUrl = new URL(context.getBaseUri().toString());
153

    
154
        // tell browser to navigate to the given URL
155
        driver.get(url.toString());
156

    
157
        takeScreenShot();
158

    
159
        if(!isOnPage()){
160
            throw new Exception("Not on the expected portal page ( current: " + driver.getCurrentUrl() + ", expected: " +  pageUrl + " )");
161
        }
162

    
163
        this.pageUrl = url;
164

    
165
        logger.info("loading " + pageUrl);
166

    
167
        // This call sets the WebElement fields.
168
        PageFactory.initElements(driver, this);
169

    
170
    }
171

    
172
    /**
173
     * Creates a new PortaPage at the WebDrivers current URL location. An Exception is thrown if
174
     * driver.getCurrentUrl() is not matching the expected URL for the specific page type.
175
     *
176
     * @param driver
177
     * @param context
178
     * @throws Exception
179
     */
180
    public PortalPage(WebDriver driver, DataPortalContext context) throws Exception {
181

    
182
        this.driver = driver;
183

    
184
        this.context = context;
185

    
186
        this.wait = new JUnitWebDriverWait(driver, 25);
187

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

    
192
        takeScreenShot();
193

    
194
        if(!isOnPage()){
195
            throw new Exception("Not on the expected portal page ( current: " + driver.getCurrentUrl() + ", expected: " +  pageUrl + " )");
196
        }
197

    
198
        // now set the real URL
199
        this.pageUrl = new URL(driver.getCurrentUrl());
200

    
201
        logger.info("loading " + pageUrl);
202

    
203
        // This call sets the WebElement fields.
204
        PageFactory.initElements(driver, this);
205

    
206
    }
207

    
208
    /**
209
     * @return
210
     */
211
    protected boolean isOnPage() {
212
        return driver.getCurrentUrl().startsWith(pageUrl.toString());
213
    }
214

    
215
    /**
216
     * navigate and reload the page if not jet there
217
     */
218
    public void get() {
219
        if(!driver.getCurrentUrl().equals(pageUrl.toString())){
220
            driver.get(pageUrl.toString());
221
            wait.until(new UrlLoaded(pageUrl.toString()));
222
            // take screenshot of new page
223
            takeScreenShot();
224
            PageFactory.initElements(driver, this);
225
        }
226
    }
227

    
228
    /**
229
     * go back in history
230
     */
231
    public void back() {
232
        driver.navigate().back();
233
    }
234

    
235
    public String getDrupalPagePath() {
236
        return drupalPagePath;
237
    }
238

    
239
    /**
240
     *
241
     * @return the page title
242
     * @deprecated use {@link driver#getTitle()}
243
     */
244
    @Deprecated
245
    public String getTitle() {
246
        return driver.getTitle();
247
    }
248

    
249
    /**
250
     * returns the warning messages from the Drupal message box
251
     * @return
252
     */
253
    public String getWarnings() {
254
        return null; //TODO unimplemented
255
    }
256

    
257
    /**
258
     * returns the error messages from the Drupal message box
259
     * @return
260
     */
261
    public String getErrors() {
262
        return null; //TODO unimplemented
263
    }
264

    
265
    public String getAuthorInformationText() {
266

    
267
        WebElement authorInformation = null;
268

    
269
        try {
270
            authorInformation  = node.findElement(By.className("submitted"));
271
        } catch (NoSuchElementException e) {
272
            // IGNORE //
273
        }
274

    
275

    
276
        if(authorInformation != null){
277
            return authorInformation.getText();
278
        } else {
279
            return null;
280
        }
281
    }
282

    
283
    public List<LinkElement> getPrimaryTabs(){
284
        List<LinkElement> tabs = new ArrayList<LinkElement>();
285
        List<WebElement> links = primaryTabs.findElements(By.tagName("a"));
286
        for(WebElement a : links) {
287
            WebElement renderedLink = a;
288
            if(renderedLink.isDisplayed()){
289
                tabs.add(new LinkElement(renderedLink));
290
            }
291
        }
292

    
293
        return tabs;
294
    }
295

    
296
    public ClassificationTreeBlock getClassificationTree() {
297
        return new ClassificationTreeBlock(classificationBrowserBlock);
298
    }
299

    
300
    public void hover(WebElement element) {
301
        Actions actions = new Actions(driver);
302
        actions.moveToElement(element, 1, 1).perform();
303
        logger.debug("hovering");
304
    }
305

    
306

    
307
    /**
308
     * Returns the current URL string from the {@link WebDriver}
309
     * @return
310
     */
311
    public URL getPageURL() {
312
        return pageUrl;
313
    }
314

    
315

    
316
    /**
317
     * return the <code>scheme://domain:port</code> part of the initial url of this page.
318
     * @return
319
     */
320
    public String getInitialUrlBase() {
321
        return pageUrl.getProtocol() + "://" + pageUrl.getHost() + pageUrl.getPort();
322
    }
323

    
324
    @Override
325
    public boolean equals(Object obj) {
326
        if (PortalPage.class.isAssignableFrom(obj.getClass())) {
327
            PortalPage page = (PortalPage) obj;
328
            return this.getPageURL().toString().equals(page.getPageURL().toString());
329

    
330
        } else {
331
            return false;
332
        }
333
    }
334

    
335

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

    
348
        if( pageType.getClass().equals(PortalPage.class) ) {
349
            throw new RuntimeException("Parameter pageType must be a subclass of PortalPage");
350
        }
351
        String targetWindow = null;
352
        List<String> targets = element.getLinkTargets(driver);
353
        if(targets.size() > 0){
354
            targetWindow = targets.get(0);
355
        }
356

    
357
        if(logger.isInfoEnabled() || logger.isDebugEnabled()){
358
            logger.info("clickLink() on " + element.toStringWithLinks());
359
        }
360
        element.getElement().click();
361
        if(targetWindow != null){
362
            driver.switchTo().window(targetWindow);
363
        }
364

    
365
        try {
366
            if(duration != null){
367
                if(waitUnit == null){
368
                    waitUnit = TimeUnit.SECONDS;
369
                }
370
                wait.withTimeout(duration, waitUnit).until(isTrue);
371
            } else {
372
                wait.until(isTrue);
373
            }
374
        } catch (AssertionError timeout){
375
            logger.info("clickLink timed out. Current WindowHandles:" + driver.getWindowHandles());
376
            throw timeout;
377
        }
378

    
379

    
380
        Constructor<T> constructor;
381
        T pageInstance;
382
        try {
383
            constructor = pageType.getConstructor(WebDriver.class, DataPortalContext.class);
384
            pageInstance = constructor.newInstance(driver, context);
385
        } catch (Exception e) {
386
            throw new RuntimeException(e);
387
        }
388
        // take screenshot of new page
389
        takeScreenShot();
390
        return pageInstance;
391
    }
392

    
393

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

    
405

    
406
    /**
407
     * replaces all underscores '_' by hyphens '-'
408
     *
409
     * @param featureName
410
     * @return
411
     */
412
    protected String normalizeClassAttribute(String featureName) {
413
        return featureName.replace('_', '-');
414
    }
415

    
416
    public File takeScreenShot(){
417

    
418
        File destFile = fileForTestMethod(new File("screenshots"));
419

    
420
        File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
421
        try {
422
            FileUtils.copyFile(scrFile, destFile);
423
            logger.info("Screenshot taken and saved as " + destFile.getAbsolutePath());
424
            return destFile;
425
        } catch (IOException e) {
426
            logger.error("could not copy sceenshot to " + destFile.getAbsolutePath(), e);
427
        }
428

    
429
        return null;
430

    
431
    }
432

    
433
    /**
434
     * Finds the test class and method in the stack trace which is using the
435
     * PortalPage and returns a File object consisting of:
436
     * {@code $targetFolder/$className/$methodName }
437
     *
438
     *
439
     * If no test class is found it will fall back to using
440
     * "noTest" as folder name and a timestamp as filename.
441
     * @return
442
     */
443
    private File fileForTestMethod(File targetFolder){
444

    
445
        StackTraceElement[] trace = Thread.currentThread().getStackTrace();
446
        for (StackTraceElement stackTraceElement : trace) {
447
            // according to the convention all test class names should end with "Test"
448
            if(stackTraceElement.getClassName().endsWith("Test")){
449
                return uniqueIndexedFile(
450
                            targetFolder.getAbsolutePath() + File.separator + stackTraceElement.getClassName(),
451
                            stackTraceElement.getMethodName(),
452
                            "png");
453

    
454
            }
455
        }
456
        return uniqueIndexedFile(
457
                targetFolder.getAbsolutePath() + File.separator + "noTest",
458
                Long.toString(System.currentTimeMillis()),
459
                "png");
460
    }
461

    
462

    
463
    private File uniqueIndexedFile(String folder, String fileName, String suffix){
464
        File file;
465
        int i = 0;
466
        while(true){
467
            file = new File(folder + File.separator + fileName + "_" + Integer.toString(i) + "." + suffix);
468
            if(!file.exists()){
469
                return file;
470
            }
471
        }
472
    }
473

    
474

    
475
}
(3-3/6)