0
# Page Object Factory
1
2
The Page Factory provides a comprehensive implementation of the Page Object Model pattern, offering annotations for element location, automatic element initialization, and flexible locator strategies. It simplifies page object creation by automatically initializing WebElement fields with proxies that locate elements dynamically.
3
4
## Core Factory Methods
5
6
```java { .api }
7
public class PageFactory {
8
/**
9
* Create and initialize a new page object instance
10
*/
11
public static <T> T initElements(WebDriver driver, Class<T> pageClassToProxy);
12
13
/**
14
* Initialize an existing page object instance with element proxies
15
*/
16
public static void initElements(WebDriver driver, Object page);
17
18
/**
19
* Initialize page object with custom element locator factory
20
*/
21
public static void initElements(ElementLocatorFactory factory, Object page);
22
23
/**
24
* Initialize page object with custom field decorator
25
*/
26
public static void initElements(FieldDecorator decorator, Object page);
27
}
28
```
29
30
### Usage Examples
31
32
```java
33
// Create and initialize new page object
34
LoginPage loginPage = PageFactory.initElements(driver, LoginPage.class);
35
36
// Initialize existing page object
37
LoginPage loginPage = new LoginPage();
38
PageFactory.initElements(driver, loginPage);
39
40
// Initialize with custom timeout for AJAX elements
41
AjaxElementLocatorFactory factory = new AjaxElementLocatorFactory(driver, 15);
42
PageFactory.initElements(factory, loginPage);
43
```
44
45
## Element Location Annotations
46
47
### @FindBy Annotation
48
49
```java { .api }
50
@Retention(RetentionPolicy.RUNTIME)
51
@Target({ElementType.FIELD, ElementType.TYPE})
52
public @interface FindBy {
53
/**
54
* Location strategy to use
55
*/
56
How how() default How.UNSET;
57
58
/**
59
* Value to use with the location strategy
60
*/
61
String using() default "";
62
63
/**
64
* Locate by ID attribute
65
*/
66
String id() default "";
67
68
/**
69
* Locate by name attribute
70
*/
71
String name() default "";
72
73
/**
74
* Locate by CSS class name
75
*/
76
String className() default "";
77
78
/**
79
* Locate by CSS selector
80
*/
81
String css() default "";
82
83
/**
84
* Locate by HTML tag name
85
*/
86
String tagName() default "";
87
88
/**
89
* Locate by exact link text
90
*/
91
String linkText() default "";
92
93
/**
94
* Locate by partial link text
95
*/
96
String partialLinkText() default "";
97
98
/**
99
* Locate by XPath expression
100
*/
101
String xpath() default "";
102
}
103
```
104
105
### @FindBys Annotation (AND Logic)
106
107
```java { .api }
108
@Retention(RetentionPolicy.RUNTIME)
109
@Target({ElementType.FIELD, ElementType.TYPE})
110
public @interface FindBys {
111
/**
112
* Array of @FindBy annotations to chain together (all must match)
113
*/
114
FindBy[] value();
115
}
116
```
117
118
### @FindAll Annotation (OR Logic)
119
120
```java { .api }
121
@Retention(RetentionPolicy.RUNTIME)
122
@Target({ElementType.FIELD, ElementType.TYPE})
123
public @interface FindAll {
124
/**
125
* Array of @FindBy annotations (any can match)
126
*/
127
FindBy[] value();
128
}
129
```
130
131
### @CacheLookup Annotation
132
133
```java { .api }
134
@Retention(RetentionPolicy.RUNTIME)
135
@Target(ElementType.FIELD)
136
public @interface CacheLookup {
137
// Marker annotation - element lookups will be cached after first access
138
}
139
```
140
141
### Location Strategy Enum
142
143
```java { .api }
144
public enum How {
145
CLASS_NAME, CSS, ID, ID_OR_NAME, LINK_TEXT, NAME,
146
PARTIAL_LINK_TEXT, TAG_NAME, XPATH, UNSET;
147
148
/**
149
* Build a By locator for this strategy with the given value
150
*/
151
public abstract By buildBy(String value);
152
}
153
```
154
155
## Page Object Examples
156
157
### Basic Page Object
158
159
```java
160
import org.openqa.selenium.WebElement;
161
import org.openqa.selenium.support.FindBy;
162
import org.openqa.selenium.support.PageFactory;
163
164
public class LoginPage {
165
@FindBy(id = "username")
166
private WebElement usernameField;
167
168
@FindBy(name = "password")
169
private WebElement passwordField;
170
171
@FindBy(css = "button[type='submit']")
172
private WebElement submitButton;
173
174
@FindBy(xpath = "//div[@class='error-message']")
175
private WebElement errorMessage;
176
177
public LoginPage(WebDriver driver) {
178
PageFactory.initElements(driver, this);
179
}
180
181
public void login(String username, String password) {
182
usernameField.clear();
183
usernameField.sendKeys(username);
184
passwordField.clear();
185
passwordField.sendKeys(password);
186
submitButton.click();
187
}
188
189
public String getErrorMessage() {
190
return errorMessage.getText();
191
}
192
}
193
```
194
195
### Advanced Locator Strategies
196
197
```java
198
public class AdvancedPage {
199
// Using How enum with using attribute
200
@FindBy(how = How.CSS, using = ".submit-btn")
201
private WebElement submitButton;
202
203
// Chained locators (AND logic) - both conditions must match
204
@FindBys({
205
@FindBy(className = "form-group"),
206
@FindBy(tagName = "input")
207
})
208
private WebElement nestedInput;
209
210
// Multiple possible locators (OR logic) - any condition can match
211
@FindAll({
212
@FindBy(id = "submit"),
213
@FindBy(name = "submit"),
214
@FindBy(xpath = "//input[@type='submit']")
215
})
216
private WebElement submitByAny;
217
218
// Cached lookup - element located once and reused
219
@CacheLookup
220
@FindBy(id = "static-header")
221
private WebElement header;
222
223
// List of elements
224
@FindBy(className = "menu-item")
225
private List<WebElement> menuItems;
226
}
227
```
228
229
## Element Locator Infrastructure
230
231
### ElementLocator Interface
232
233
```java { .api }
234
public interface ElementLocator {
235
/**
236
* Find a single element
237
*/
238
WebElement findElement();
239
240
/**
241
* Find multiple elements
242
*/
243
List<WebElement> findElements();
244
}
245
```
246
247
### ElementLocatorFactory Interface
248
249
```java { .api }
250
public interface ElementLocatorFactory {
251
/**
252
* Create an ElementLocator for the given field
253
*/
254
ElementLocator createLocator(Field field);
255
}
256
```
257
258
### DefaultElementLocator
259
260
```java { .api }
261
public class DefaultElementLocator implements ElementLocator {
262
public DefaultElementLocator(SearchContext searchContext, Field field);
263
public DefaultElementLocator(SearchContext searchContext, AbstractAnnotations annotations);
264
265
public WebElement findElement();
266
public List<WebElement> findElements();
267
protected boolean shouldCache();
268
}
269
```
270
271
### AjaxElementLocator
272
273
```java { .api }
274
public class AjaxElementLocator extends DefaultElementLocator {
275
/**
276
* Element locator that waits for elements to appear (for AJAX content)
277
*/
278
public AjaxElementLocator(SearchContext context, int timeOutInSeconds, AbstractAnnotations annotations);
279
public AjaxElementLocator(SearchContext searchContext, Field field, int timeOutInSeconds);
280
281
public WebElement findElement();
282
public List<WebElement> findElements();
283
protected long sleepFor();
284
protected boolean isElementUsable(WebElement element);
285
}
286
```
287
288
### AjaxElementLocatorFactory
289
290
```java { .api }
291
public class AjaxElementLocatorFactory implements ElementLocatorFactory {
292
/**
293
* Factory for creating AjaxElementLocator instances with timeout
294
*/
295
public AjaxElementLocatorFactory(SearchContext searchContext, int timeOutInSeconds);
296
297
public ElementLocator createLocator(Field field);
298
}
299
```
300
301
## Field Decoration
302
303
### FieldDecorator Interface
304
305
```java { .api }
306
public interface FieldDecorator {
307
/**
308
* Decorate a field with a proxy or return null if field should not be decorated
309
*/
310
Object decorate(ClassLoader loader, Field field);
311
}
312
```
313
314
### DefaultFieldDecorator
315
316
```java { .api }
317
public class DefaultFieldDecorator implements FieldDecorator {
318
public DefaultFieldDecorator(ElementLocatorFactory factory);
319
320
public Object decorate(ClassLoader loader, Field field);
321
protected boolean isDecoratableList(Field field);
322
protected WebElement proxyForLocator(ClassLoader loader, ElementLocator locator);
323
protected List<WebElement> proxyForListLocator(ClassLoader loader, ElementLocator locator);
324
}
325
```
326
327
## Advanced Locator Classes
328
329
### ByAll (OR Logic)
330
331
```java { .api }
332
public class ByAll extends By {
333
/**
334
* Locator that finds elements matching any of multiple By instances
335
*/
336
public ByAll(By... bys);
337
338
public WebElement findElement(SearchContext context);
339
public List<WebElement> findElements(SearchContext context);
340
}
341
```
342
343
### ByChained (AND Logic)
344
345
```java { .api }
346
public class ByChained extends By {
347
/**
348
* Locator that finds elements using chained lookups
349
*/
350
public ByChained(By... bys);
351
352
public WebElement findElement(SearchContext context);
353
public List<WebElement> findElements(SearchContext context);
354
}
355
```
356
357
## Annotation Processing
358
359
### AbstractAnnotations
360
361
```java { .api }
362
public abstract class AbstractAnnotations {
363
/**
364
* Build a By locator from field annotations
365
*/
366
public abstract By buildBy();
367
368
/**
369
* Check if element lookup should be cached
370
*/
371
public abstract boolean isLookupCached();
372
}
373
```
374
375
### Annotations
376
377
```java { .api }
378
public class Annotations extends AbstractAnnotations {
379
/**
380
* Standard implementation for processing Page Object annotations
381
*/
382
public Annotations(Field field);
383
384
public boolean isLookupCached();
385
public By buildBy();
386
protected Field getField();
387
protected By buildByFromDefault();
388
protected void assertValidAnnotations();
389
}
390
```
391
392
### AbstractFindByBuilder
393
394
```java { .api }
395
public abstract class AbstractFindByBuilder {
396
/**
397
* Abstract base class for building By instances from annotations
398
*/
399
public abstract By buildIt(Object annotation, Field field);
400
401
protected By buildByFromFindBy(FindBy findBy);
402
protected By buildByFromShortFindBy(FindBy findBy);
403
protected By buildByFromLongFindBy(FindBy findBy);
404
protected void assertValidFindBy(FindBy findBy);
405
protected void assertValidFindBys(FindBys findBys);
406
protected void assertValidFindAll(FindAll findAll);
407
}
408
```
409
410
## Usage Patterns
411
412
### AJAX-Enabled Page Objects
413
414
```java
415
public class DynamicPage {
416
private WebDriver driver;
417
418
public DynamicPage(WebDriver driver) {
419
this.driver = driver;
420
// Use AjaxElementLocatorFactory for dynamic content
421
AjaxElementLocatorFactory factory = new AjaxElementLocatorFactory(driver, 15);
422
PageFactory.initElements(factory, this);
423
}
424
425
@FindBy(id = "dynamic-content")
426
private WebElement dynamicContent;
427
428
@FindBy(className = "async-loaded")
429
private List<WebElement> asyncElements;
430
}
431
```
432
433
### Inheritance and Page Object Hierarchies
434
435
```java
436
public abstract class BasePage {
437
@FindBy(id = "header")
438
protected WebElement header;
439
440
@FindBy(id = "footer")
441
protected WebElement footer;
442
443
public BasePage(WebDriver driver) {
444
PageFactory.initElements(driver, this);
445
}
446
}
447
448
public class HomePage extends BasePage {
449
@FindBy(id = "welcome-message")
450
private WebElement welcomeMessage;
451
452
public HomePage(WebDriver driver) {
453
super(driver);
454
}
455
}
456
```