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
import static org.junit.Assert.assertTrue;
5

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

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

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

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

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

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

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

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

    
56
    protected WebDriver driver;
57

    
58
    protected DataPortalContext context;
59

    
60
    protected final JUnitWebDriverWait wait;
61

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

    
66

    
67
    /**
68
     * Implementations of this method will supply the relative
69
     * path to the Drupal page. This path will usally have the form
70
     * <code>cdm_dataportal/{nodetype}</code>. For example the taxon pages all
71
     * have the page base <code>cdm_dataportal/taxon</code>
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
//    @FindBys({
104
//        @FindBy(className="messages"),
105
//        @FindBy(className="error")}
106
//        )
107
    @FindBy(className="messages")
108
    @CacheLookup
109
    protected WebElement messagesErrorCichorieaeTheme;
110

    
111
    @FindBy(className="messages_error")
112
    @CacheLookup
113
    protected WebElement messagesErrorOtherThemes;
114

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

    
128
        this.driver = driver;
129

    
130
        this.context = context;
131

    
132
        this.wait = new JUnitWebDriverWait(driver, WAIT_SECONDS);
133

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

    
136
        this.pageUrl = new URL(context.getBaseUri().toString() + DRUPAL_PAGE_QUERY_BASE + drupalPagePath);
137

    
138
        // tell browser to navigate to the page
139
        driver.get(pageUrl.toString());
140

    
141
        takeScreenShot();
142

    
143
        // This call sets the WebElement fields.
144
        PageFactory.initElements(driver, this);
145

    
146
        logger.info("loading " + pageUrl);
147

    
148
        try {
149
            assertTrue("The page must not show an error box", !messagesErrorCichorieaeTheme.getAttribute("class").contains("error") && messagesErrorCichorieaeTheme.getText() == null);
150
        } catch (NoSuchElementException e) {
151
            //IGNORE since this is expected!
152
        }
153
        try {
154
            assertNull("The page must not show an error box", messagesErrorOtherThemes.getText());
155
        } catch (NoSuchElementException e) {
156
            //IGNORE since this is expected!
157
        }
158

    
159
    }
160

    
161
    /**
162
     * Creates a new PortaPage at given URL location. An Exception is thrown if
163
     * this URL is not matching the expected URL for the specific page type.
164
     *
165
     */
166
    public PortalPage(WebDriver driver, DataPortalContext context, URL url) throws Exception {
167

    
168
        this.driver = driver;
169

    
170
        this.context = context;
171

    
172
        this.wait = new JUnitWebDriverWait(driver, 25);
173

    
174
        this.pageUrl = new URL(context.getBaseUri().toString());
175

    
176
        // tell browser to navigate to the given URL
177
        driver.get(url.toString());
178

    
179
        takeScreenShot();
180

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

    
185
        this.pageUrl = url;
186

    
187
        logger.info("loading " + pageUrl);
188

    
189
        // This call sets the WebElement fields.
190
        PageFactory.initElements(driver, this);
191

    
192
    }
193

    
194
    /**
195
     * Creates a new PortaPage at the WebDrivers current URL location. An Exception is thrown if
196
     * driver.getCurrentUrl() is not matching the expected URL for the specific page type.
197
     *
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
    protected boolean isOnPage() {
229
        return driver.getCurrentUrl().startsWith(pageUrl.toString());
230
    }
231

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

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

    
252
    public String getDrupalPagePath() {
253
        return drupalPagePath;
254
    }
255

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

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

    
273
    /**
274
     * @return the error messages from the Drupal message box
275
     */
276
    public String getErrors() {
277
        return null; //TODO unimplemented
278
    }
279

    
280
    public String getAuthorInformationText() {
281

    
282
        WebElement authorInformation = null;
283

    
284
        try {
285
            authorInformation  = node.findElement(By.className("submitted"));
286
        } catch (NoSuchElementException e) {
287
            // IGNORE //
288
        }
289

    
290

    
291
        if(authorInformation != null){
292
            return authorInformation.getText();
293
        } else {
294
            return null;
295
        }
296
    }
297

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

    
308
        return tabs;
309
    }
310

    
311
    public ClassificationTreeBlock getClassificationTree() {
312
        return new ClassificationTreeBlock(classificationBrowserBlock);
313
    }
314

    
315
    public void hover(WebElement element) {
316
        Actions actions = new Actions(driver);
317
        actions.moveToElement(element, 1, 1).perform();
318
        logger.debug("hovering");
319
    }
320

    
321

    
322
    /**
323
     * @return the current URL string from the {@link WebDriver}
324
     */
325
    public URL getPageURL() {
326
        return pageUrl;
327
    }
328

    
329

    
330
    /**
331
     * @return the <code>scheme://domain:port</code> part of the initial url of this page.
332
     */
333
    public String getInitialUrlBase() {
334
        return pageUrl.getProtocol() + "://" + pageUrl.getHost() + pageUrl.getPort();
335
    }
336

    
337
    @Override
338
    public boolean equals(Object obj) {
339
        if (PortalPage.class.isAssignableFrom(obj.getClass())) {
340
            PortalPage page = (PortalPage) obj;
341
            return this.getPageURL().toString().equals(page.getPageURL().toString());
342

    
343
        } else {
344
            return false;
345
        }
346
    }
347

    
348

    
349
    /**
350
     * @param isTrue see {@link org.openqa.selenium.support.ui.FluentWait#until(Function)}
351
     * @param pageType the return type
352
     * @param duration may be null, if this in null <code>waitUnit</code> will be ignored.
353
     * @param waitUnit may be null, is ignored if <code>duration</code> is null defaults to {@link TimeUnit#SECONDS}
354

    
355
     */
356
    public <T extends PortalPage> T clickLink(BaseElement element, Function<? super WebDriver, Boolean> isTrue, Class<T> pageType, Long duration, TimeUnit waitUnit) {
357

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

    
367
        if(logger.isInfoEnabled() || logger.isDebugEnabled()){
368
            logger.info("clickLink() on " + element.toStringWithLinks());
369
        }
370
        element.getElement().click();
371
        if(targetWindow != null){
372
            driver.switchTo().window(targetWindow);
373
        }
374

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

    
389

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

    
403

    
404
    /**
405
     * @param isTrue see {@link org.openqa.selenium.support.ui.FluentWait#until(Function)}
406
     * @param type the return type
407

    
408
     */
409
    public <T extends PortalPage> T clickLink(BaseElement element, Function<? super WebDriver, Boolean> isTrue, Class<T> type) {
410
        return clickLink(element, isTrue, type, null, null);
411
    }
412

    
413

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

    
421
    public File takeScreenShot(){
422

    
423

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

    
438
        return null;
439

    
440
    }
441

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

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

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

    
473

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

    
485

    
486
}
(3-3/6)