1 package com.atlassian.pageobjects.binder;
2
3 import com.atlassian.pageobjects.Page;
4 import com.atlassian.pageobjects.PageBinder;
5 import com.atlassian.pageobjects.ProductInstance;
6 import com.atlassian.pageobjects.TestedProduct;
7 import com.atlassian.pageobjects.Tester;
8 import com.atlassian.pageobjects.browser.Browser;
9 import com.atlassian.pageobjects.browser.IgnoreBrowser;
10 import com.atlassian.pageobjects.browser.RequireBrowser;
11 import com.atlassian.pageobjects.inject.ConfigurableInjectionContext;
12 import com.google.inject.Binder;
13 import com.google.inject.Module;
14 import org.hamcrest.CoreMatchers;
15 import org.junit.Before;
16 import org.junit.Test;
17 import org.junit.runner.RunWith;
18 import org.mockito.Mock;
19 import org.mockito.runners.MockitoJUnitRunner;
20
21 import javax.inject.Inject;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertThat;
27 import static org.junit.Assert.assertTrue;
28 import static org.mockito.Mockito.verify;
29 import static org.mockito.Mockito.when;
30
31 @SuppressWarnings("unchecked")
32 @RunWith(MockitoJUnitRunner.class)
33 public class TestInjectPageBinder
34 {
35 private MyTestedProduct product;
36
37 @Mock
38 private ProductInstance productInstance;
39
40 @Mock
41 private Tester tester;
42
43 @Before
44 public void setUp() throws Exception
45 {
46 product = new MyTestedProduct(new SetTester());
47 }
48
49 @Test
50 public void testInject()
51 {
52 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
53 OneFieldPage page = binder.bind(OneFieldPage.class);
54 assertEquals("Bob", page.name.getValue());
55 }
56
57 @Test
58 public void testInjectDefaults()
59 {
60 PageBinder binder = createBinder();
61 DefaultsPage page = binder.bind(DefaultsPage.class);
62 assertNotNull(page.testedProduct);
63 assertNotNull(page.myTestedProduct);
64 assertNotNull(page.tester);
65 assertNotNull(page.setTester);
66 assertNotNull(page.pageBinder);
67 assertNotNull(page.productInstance);
68 }
69
70 @Test(expected = IllegalArgumentException.class)
71 public void testInjectMissing()
72 {
73 PageBinder binder = createBinder();
74 binder.bind(OneFieldPage.class);
75 }
76
77 @Test
78 public void testInjectWithArgument()
79 {
80 PageBinder binder = createBinder();
81 ConstructorArgumentPage page = binder.bind(ConstructorArgumentPage.class, "foo");
82 assertEquals("foo", page.name);
83 }
84
85 @Test
86 public void testInstantiateWithPrimitiveArguments()
87 {
88 PageBinder binder = createBinder();
89 ConstructorArgumentPrimitive object = binder.bind(ConstructorArgumentPrimitive.class, 5, true);
90 assertNotNull(object);
91 assertEquals(5, object.intField);
92 assertTrue(object.booleanField);
93 }
94
95 @Test
96 public void testInjectWithArgumentSubclass()
97 {
98 PageBinder binder = createBinder();
99 ConstructorArgumentPage page = binder.bind(ConstructorArgumentPage.class, 43);
100 assertEquals(43, page.age);
101 }
102
103 @Test
104 public void testInitAfterInject()
105 {
106 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
107 OneFieldWithInitPage page = binder.bind(OneFieldWithInitPage.class);
108 assertEquals("Bob Jones", page.name);
109 }
110
111 @Test
112 public void testPrivateInitAfterInject()
113 {
114 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
115 OneFieldWithPrivateInitPage page = binder.bind(OneFieldWithPrivateInitPage.class);
116 assertEquals("Bob Private", page.name);
117 }
118
119 @Test
120 public void testOneFieldWithSuperClassInit()
121 {
122 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
123 OneFieldWithSuperClassInitPage page = binder.bind(OneFieldWithSuperClassInitPage.class);
124 assertEquals("Bob Private", page.getName());
125
126 }
127
128 @Test
129 public void testProtectedInitAfterInject()
130 {
131 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
132 OneFieldWithProtectedInitPage page = binder.bind(OneFieldWithProtectedInitPage.class);
133 assertEquals("Bob Protected", page.name);
134 }
135
136 @Test
137 public void testParentInject()
138 {
139 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
140 ChildNoNamePage page = binder.bind(ChildNoNamePage.class);
141 assertEquals("Bob", page.name.getValue());
142 }
143
144 @Test
145 public void shouldImplementConfigurableInjectionContext()
146 {
147 final PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
148 assertThat(binder, CoreMatchers.instanceOf(ConfigurableInjectionContext.class));
149 assertEquals("Bob", binder.bind(OneFieldPage.class).name.getValue());
150 ConfigurableInjectionContext.class.cast(binder)
151 .configure()
152 .addImplementation(StringField.class, AnotherStringFieldImpl.class)
153 .finish();
154 assertEquals("Rob", binder.bind(OneFieldPage.class).name.getValue());
155 }
156
157 @Test
158 public void shouldAllowConfiguringNewSingletonInstanceThatIsSubclassOfInterfaceType()
159 {
160 final PageBinder binder = createBinder();
161 ConfigurableInjectionContext.class.cast(binder)
162 .configure()
163 .addSingleton(StringField.class, new StringFieldImpl())
164 .finish();
165 assertEquals("Bob", binder.bind(OneFieldPage.class).name.getValue());
166 }
167
168 @Test
169 public void shouldAllowConfiguringNewImplementationInstance()
170 {
171 PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
172 assertEquals("Bob", binder.bind(OneFieldPage.class).name.getValue());
173 ConfigurableInjectionContext.class.cast(binder)
174 .configure()
175 .addSingleton(StringField.class, new StringField()
176 {
177 @Override
178 public String getValue()
179 {
180 return "Boom!";
181 }
182 })
183 .finish();
184 assertEquals("Boom!", binder.bind(OneFieldPage.class).name.getValue());
185 }
186
187 @Test
188 public void shouldIncludePostInjectionProcessorsAddedViaInjectionContext()
189 {
190 PageBinder binder = createBinder();
191 assertEquals("Default", binder.bind(MutablePage.class).getValue());
192
193 ConfigurableInjectionContext.class.cast(binder)
194 .configure()
195 .addSingleton(MutablePageProcessor.class, new MutablePageProcessor())
196 .finish();
197
198
199 assertEquals("Post processed", binder.bind(MutablePage.class).getValue());
200 }
201
202 @Test
203 public void visitUrlShouldRemoveExtraSlashAfterHostname() throws Exception
204 {
205 when(productInstance.getBaseUrl()).thenReturn("http://localhost/");
206
207 final PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
208 binder.navigateToAndBind(OneFieldPage.class);
209
210 verify(tester).gotoUrl("http://localhost/path");
211 }
212
213 @Test
214 public void visitUrlShouldAddMissingSlashAfterHostname() throws Exception
215 {
216 when(productInstance.getBaseUrl()).thenReturn("http://localhost");
217
218 final PageBinder binder = createBinder(StringField.class, StringFieldImpl.class);
219 binder.navigateToAndBind(PageWithNoLeadingSlash.class);
220
221 verify(tester).gotoUrl("http://localhost/path");
222 }
223
224 @Test
225 public void shouldCheckForIgnoreBrowserAndRequireBrowserAnnotation()
226 {
227
228 PageObjectWithRequiredAndIgnoredBrowsers pageObject = createBinderWithBrowser(Browser.FIREFOX)
229 .bind(PageObjectWithRequiredAndIgnoredBrowsers.class);
230
231 assertTrue(pageObject.initIgnoredInvoked);
232 assertTrue(pageObject.initRequiredInvoked);
233
234
235 pageObject = createBinderWithBrowser(Browser.HTMLUNIT).bind(PageObjectWithRequiredAndIgnoredBrowsers.class);
236
237 assertFalse(pageObject.initIgnoredInvoked);
238 assertTrue(pageObject.initRequiredInvoked);
239
240
241 pageObject = createBinderWithBrowser(Browser.SAFARI).bind(PageObjectWithRequiredAndIgnoredBrowsers.class);
242
243 assertTrue(pageObject.initIgnoredInvoked);
244 assertFalse(pageObject.initRequiredInvoked);
245
246
247 pageObject = createBinderWithBrowser(Browser.HTMLUNIT_NOJS).bind(PageObjectWithRequiredAndIgnoredBrowsers.class);
248
249 assertFalse(pageObject.initIgnoredInvoked);
250 assertFalse(pageObject.initRequiredInvoked);
251 }
252
253 @Test
254 public void shouldSupportIgnoreAllAndRequireAll()
255 {
256 for (Browser browser : Browser.values())
257 {
258 PageObjectWithRequiredAndIgnoredBrowsers pageObject = createBinderWithBrowser(browser)
259 .bind(PageObjectWithRequiredAndIgnoredBrowsers.class);
260
261 assertFalse(pageObject.initIgnoreAllInvoked);
262 assertTrue(pageObject.initRequireAllInvoked);
263 }
264 }
265
266 private InjectPageBinder createBinder()
267 {
268 return createBinder(null, null);
269 }
270
271 private InjectPageBinder createBinder(final Class<?> key, final Class impl)
272 {
273 return new InjectPageBinder(productInstance, tester, new StandardModule(product),
274 new Module()
275 {
276 public void configure(Binder binder)
277 {
278 if (key != null)
279 {
280 binder.bind(key).to(impl);
281 }
282 }
283 });
284 }
285
286 private InjectPageBinder createBinderWithBrowser(Browser browser)
287 {
288 InjectPageBinder pageBinder = createBinder();
289 ConfigurableInjectionContext.class.cast(pageBinder).configure()
290 .addSingleton(Browser.class, browser).finish();
291
292 return pageBinder;
293 }
294
295 static class AbstractPage implements Page
296 {
297 public String getUrl()
298 {
299 return "/path";
300 }
301 }
302
303 static class OneFieldPage extends AbstractPage
304 {
305 @Inject
306 private StringField name;
307 }
308
309 static class PageWithNoLeadingSlash extends AbstractPage
310 {
311 @Override
312 public String getUrl()
313 {
314 return "path";
315 }
316 }
317
318 static class ConstructorArgumentPrimitive
319 {
320 private final int intField;
321 private final boolean booleanField;
322
323 public ConstructorArgumentPrimitive(int intArg, boolean booleanArg)
324 {
325 this.intField = intArg;
326 this.booleanField = booleanArg;
327 }
328 }
329
330 static class ConstructorArgumentPage extends AbstractPage
331 {
332 private final String name;
333 private final Number age;
334
335 @SuppressWarnings("UnusedDeclaration")
336 public ConstructorArgumentPage(String name)
337 {
338 this.name = name;
339 this.age = null;
340 }
341
342 @SuppressWarnings("UnusedDeclaration")
343 public ConstructorArgumentPage(Number age)
344 {
345 this.age = age;
346 this.name = null;
347 }
348 }
349
350 static class ParentNamePage extends AbstractPage
351 {
352 @Inject
353 protected StringField name;
354 }
355
356 static class ChildNoNamePage extends ParentNamePage
357 {
358 }
359
360 static class DefaultsPage extends AbstractPage
361 {
362 @Inject
363 private ProductInstance productInstance;
364
365 @Inject
366 private TestedProduct testedProduct;
367
368 @Inject
369 private MyTestedProduct myTestedProduct;
370
371 @Inject
372 private Tester tester;
373
374 @Inject
375 private SetTester setTester;
376
377 @Inject
378 private PageBinder pageBinder;
379 }
380
381 static class OneFieldWithInitPage extends AbstractPage
382 {
383 @Inject
384 private StringField field;
385
386 private String name;
387 @Init
388 public void init()
389 {
390 name = field.getValue() + " Jones";
391 }
392 }
393
394 static interface StringField
395 {
396 String getValue();
397 }
398
399 static class StringFieldImpl implements StringField
400 {
401 public String getValue()
402 {
403 return "Bob";
404 }
405 }
406
407 static class AnotherStringFieldImpl implements StringField
408 {
409 public String getValue()
410 {
411 return "Rob";
412 }
413 }
414
415 static class OneFieldWithPrivateInitPage extends AbstractPage
416 {
417 @Inject
418 private StringField field;
419
420 private String name;
421
422 @Init
423 @SuppressWarnings("UnusedDeclaration")
424 private void init()
425 {
426 name = field.getValue() + " Private";
427 }
428
429 public String getName()
430 {
431 return name;
432 }
433 }
434
435 static class OneFieldWithProtectedInitPage extends AbstractPage
436 {
437 @Inject
438 private StringField field;
439
440 private String name;
441
442 @Init
443 protected void init()
444 {
445 name = field.getValue() + " Protected";
446 }
447 }
448
449 static class OneFieldWithSuperClassInitPage extends OneFieldWithPrivateInitPage
450 {
451 }
452
453 static class MutablePage
454 {
455 private String value = "Default";
456
457 public String getValue()
458 {
459 return value;
460 }
461
462 public void setValue(String value)
463 {
464 this.value = value;
465 }
466 }
467
468 static class MutablePageProcessor implements PostInjectionProcessor
469 {
470 @Override
471 public <T> T process(T pageObject)
472 {
473 if (pageObject instanceof MutablePage)
474 {
475 MutablePage.class.cast(pageObject).setValue("Post processed");
476 }
477 return pageObject;
478 }
479 }
480
481 static class PageObjectWithRequiredAndIgnoredBrowsers
482 {
483 boolean initIgnoredInvoked;
484 boolean initRequiredInvoked;
485
486 boolean initIgnoreAllInvoked;
487 boolean initRequireAllInvoked;
488
489 @Init
490 @IgnoreBrowser(Browser.ALL)
491 public void initIgnoreAll()
492 {
493 initIgnoreAllInvoked = true;
494 }
495
496 @Init
497 @IgnoreBrowser({ Browser.HTMLUNIT, Browser.HTMLUNIT_NOJS })
498 public void initIgnored()
499 {
500 initIgnoredInvoked = true;
501 }
502
503 @Init
504 @RequireBrowser(Browser.ALL)
505 public void initRequireAll()
506 {
507 initRequireAllInvoked = true;
508 }
509
510 @Init
511 @RequireBrowser({ Browser.CHROME, Browser.FIREFOX, Browser.HTMLUNIT })
512 public void initRequired()
513 {
514 initRequiredInvoked = true;
515 }
516 }
517
518 }