1   package com.atlassian.plugins.codegen;
2   
3   import com.atlassian.plugins.codegen.ComponentDeclaration.Visibility;
4   import com.atlassian.plugins.codegen.XmlMatchers.XmlWrapper;
5   
6   import com.google.common.collect.ImmutableMap;
7   
8   import org.apache.commons.io.FileUtils;
9   import org.junit.Before;
10  import org.junit.Test;
11  
12  import static com.atlassian.fugue.Option.some;
13  import static com.atlassian.plugins.codegen.ClassId.fullyQualified;
14  import static com.atlassian.plugins.codegen.I18nString.i18nString;
15  import static com.atlassian.plugins.codegen.ModuleDescriptor.moduleDescriptor;
16  import static com.atlassian.plugins.codegen.PluginParameter.pluginParameter;
17  import static com.atlassian.plugins.codegen.PluginProjectChangeset.changeset;
18  import static com.atlassian.plugins.codegen.XmlMatchers.node;
19  import static com.atlassian.plugins.codegen.XmlMatchers.nodeCount;
20  import static com.atlassian.plugins.codegen.XmlMatchers.nodeName;
21  import static com.atlassian.plugins.codegen.XmlMatchers.nodeTextEquals;
22  import static com.atlassian.plugins.codegen.XmlMatchers.nodes;
23  import static org.hamcrest.MatcherAssert.assertThat;
24  import static org.hamcrest.Matchers.equalTo;
25  import static org.hamcrest.Matchers.notNullValue;
26  import static org.hamcrest.Matchers.nullValue;
27  
28  public class PluginXmlRewriterTest
29  {
30      protected static final String CLASS = "com.atlassian.test.MyClass";
31      protected static final String INTERFACE = "com.atlassian.test.MyInterface";
32      protected static final ComponentImport IMPORT = ComponentImport.componentImport(fullyQualified(INTERFACE));
33      
34      protected ComponentDeclaration.Builder componentBuilder = ComponentDeclaration.builder(fullyQualified(CLASS), "my-key");
35  
36      protected ProjectHelper helper;
37      protected PluginXmlRewriter rewriter;
38      
39      @Before
40      public void setup() throws Exception
41      {
42          helper = new ProjectHelper();
43          usePluginXml("empty-plugin.xml");
44      }
45      
46      private void usePluginXml(String path) throws Exception
47      {
48          helper.usePluginXml(path);
49          rewriter = new PluginXmlRewriter(helper.location);
50      }
51      
52      @Test
53      public void i18nResourceIsNotAddedByDefault() throws Exception
54      {
55          assertThat(applyChanges(addPluginParam()),
56                     nodes("//resource[@type='i18n']", nodeCount(0)));
57      }
58  
59      @Test
60      public void i18nResourceIsAddedIfI18nPropertiesArePresent() throws Exception
61      {
62          assertThat(applyChanges(addI18nProperty()),
63                     nodes("//resource[@type='i18n']", nodeCount(1)));
64      }
65  
66      @Test
67      public void i18nResourceHasDefaultName() throws Exception
68      {
69          assertThat(applyChanges(addI18nProperty()),
70                     node("//resource[@type='i18n']/@name", nodeTextEquals("i18n")));
71      }
72  
73      @Test
74      public void i18nResourceHasDefaultLocation() throws Exception
75      {
76          assertThat(applyChanges(addI18nProperty()),
77                     node("//resource[@type='i18n']/@location", nodeTextEquals(ProjectHelper.PLUGIN_KEY)));
78      }
79  
80      @Test
81      public void i18nResourceWithSameNameIsNotAdded() throws Exception
82      {
83          usePluginXml("plugin-with-same-i18n-name.xml");
84          
85          assertThat(applyChanges(addI18nProperty()),
86                     nodes("//resource[@type='i18n']", nodeCount(1)));
87      }
88      
89      @Test
90      public void i18nResourceWithSameNameIsNotOverwritten() throws Exception
91      {
92          usePluginXml("plugin-with-same-i18n-name.xml");
93  
94          assertThat(applyChanges(addI18nProperty()),
95                     node("//resource[@type='i18n']/@location", nodeTextEquals("nonstandard-location")));
96      }
97  
98      @Test
99      public void i18nResourceWithSameLocationIsNotAdded() throws Exception
100     {
101         usePluginXml("plugin-with-same-i18n-location.xml");
102         
103         assertThat(applyChanges(addI18nProperty()),
104                    nodes("//resource[@type='i18n']", nodeCount(1)));
105     }
106     
107     @Test
108     public void i18nResourceWithSameLocationIsNotOverwritten() throws Exception
109     {
110         usePluginXml("plugin-with-same-i18n-location.xml");
111 
112         assertThat(applyChanges(addI18nProperty()),
113                    node("//resource[@type='i18n']/@name", nodeTextEquals("nonstandard-name")));
114     }
115     
116     @Test
117     public void pluginParamIsAdded() throws Exception
118     {
119         assertThat(applyChanges(addPluginParam()),
120                    nodes("//plugin-info/param", nodeCount(1)));
121     }
122     
123     @Test
124     public void pluginParamHasName() throws Exception
125     {
126         assertThat(applyChanges(addPluginParam()),
127                    node("//plugin-info/param/@name", nodeTextEquals("foo")));
128     }
129 
130     @Test
131     public void pluginParamHasValue() throws Exception
132     {
133         assertThat(applyChanges(addPluginParam()),
134                    node("//plugin-info/param", nodeTextEquals("bar")));
135     }
136 
137     @Test
138     public void secondPluginParamIsAdded() throws Exception
139     {
140         applyChanges(addPluginParam());
141 
142         assertThat(applyChanges(changeset().with(pluginParameter("second", "thing"))),
143                    nodes("//plugin-info/param", nodeCount(2)));
144     }
145 
146     @Test
147     public void secondPluginParamHasValue() throws Exception
148     {
149         applyChanges(addPluginParam());
150 
151         assertThat(applyChanges(changeset().with(pluginParameter("second", "thing"))),
152                    node("//plugin-info/param[@name='second']", nodeTextEquals("thing")));
153     }
154 
155     @Test
156     public void duplicatePluginParamIsNotAdded() throws Exception
157     {
158         applyChanges(addPluginParam());
159 
160         assertThat(applyChanges(changeset().with(pluginParameter("foo", "baz"))),
161                    nodes("//plugin-info/param", nodeCount(1)));
162     }
163 
164     @Test
165     public void pluginParamCannotBeOverwritten() throws Exception
166     {
167         applyChanges(addPluginParam());
168 
169         assertThat(applyChanges(changeset().with(pluginParameter("second", "thing"))),
170                    node("//plugin-info/param", nodeTextEquals("bar")));
171     }
172     
173     @Test
174     public void componentImportIsAdded() throws Exception
175     {
176         assertThat(applyChanges(changeset().with(IMPORT)),
177                    node("//component-import", notNullValue()));
178     }
179 
180     @Test
181     public void componentImportHasInterface() throws Exception
182     {
183         assertThat(applyChanges(changeset().with(IMPORT)),
184                    node("//component-import/@interface", nodeTextEquals(INTERFACE)));
185     }
186 
187     @Test
188     public void componentImportHasDefaultKey() throws Exception
189     {
190         assertThat(applyChanges(changeset().with(IMPORT)),
191                    node("//component-import/@key", nodeTextEquals("myInterface")));
192     }
193     
194     @Test
195     public void componentImportHasSpecifiedKey() throws Exception
196     {
197         assertThat(applyChanges(changeset().with(IMPORT.key(some("new-key")))),
198                    node("//component-import/@key", nodeTextEquals("new-key")));
199     }
200     
201     @Test
202     public void componentImportHasNoFilterByDefault() throws Exception
203     {
204         assertThat(applyChanges(changeset().with(IMPORT)),
205                    node("//component-import/@filter", nullValue()));
206     }
207     
208     @Test
209     public void componentImportHasSpecifiedFilter() throws Exception
210     {
211         assertThat(applyChanges(changeset().with(IMPORT.filter(some("my-filter")))),
212                    node("//component-import/@filter", nodeTextEquals("my-filter")));
213     }
214     
215     @Test
216     public void duplicateComponentImportKeyCannotBeAdded() throws Exception
217     {
218         ComponentImport secondImportWithSameKey = IMPORT.filter(some("different"));
219         applyChanges(changeset().with(IMPORT));
220 
221         assertThat(applyChanges(changeset().with(secondImportWithSameKey)),
222                    nodes("//component-import", nodeCount(1)));
223     }
224 
225     @Test
226     public void componentImportWithSameKeyIsNotOverwritten() throws Exception
227     {
228         ComponentImport secondImportWithSameKey = IMPORT.filter(some("different"));
229         applyChanges(changeset().with(IMPORT));
230 
231         assertThat(applyChanges(changeset().with(secondImportWithSameKey)),
232                    node("//component-import/@filter", nullValue()));
233     }
234     
235     @Test
236     public void componentIsAdded() throws Exception
237     {
238         assertThat(applyChanges(changeset().with(componentBuilder.build())),
239                    node("//component", notNullValue()));
240     }
241 
242     @Test
243     public void componentHasKey() throws Exception
244     {
245         assertThat(applyChanges(changeset().with(componentBuilder.build())),
246                    node("//component/@key", nodeTextEquals("my-key")));
247     }
248 
249     @Test
250     public void componentHasClass() throws Exception
251     {
252         assertThat(applyChanges(changeset().with(componentBuilder.build())),
253                    node("//component/@class", nodeTextEquals(CLASS)));
254     }
255 
256     @Test
257     public void componentHasNoNameByDefault() throws Exception
258     {
259         assertThat(applyChanges(changeset().with(componentBuilder.build())),
260                    node("//component/@name", nullValue()));
261     }
262 
263     @Test
264     public void componentHasSpecifiedName() throws Exception
265     {
266         componentBuilder.name(some("my-name"));
267         
268         assertThat(applyChanges(changeset().with(componentBuilder.build())),
269                    node("//component/@name", nodeTextEquals("my-name")));
270     }
271 
272     @Test
273     public void componentHasNoNameI18nKeyByDefault() throws Exception
274     {
275         assertThat(applyChanges(changeset().with(componentBuilder.build())),
276                    node("//component/@i18n-name-key", nullValue()));
277     }
278 
279     @Test
280     public void componentHasSpecifiedNameI18nKey() throws Exception
281     {
282         componentBuilder.nameI18nKey(some("name-key"));
283         
284         assertThat(applyChanges(changeset().with(componentBuilder.build())),
285                    node("//component/@i18n-name-key", nodeTextEquals("name-key")));
286     }
287 
288     @Test
289     public void componentIsPrivateByDefault() throws Exception
290     {
291         assertThat(applyChanges(changeset().with(componentBuilder.build())),
292                    node("//component/@public", nullValue()));
293     }
294 
295     @Test
296     public void componentIsPublicIfSpecified() throws Exception
297     {
298         componentBuilder.visibility(Visibility.PUBLIC);
299         
300         assertThat(applyChanges(changeset().with(componentBuilder.build())),
301                    node("//component/@public", nodeTextEquals("true")));
302     }
303 
304     @Test
305     public void componentHasNoAliasByDefault() throws Exception
306     {
307         assertThat(applyChanges(changeset().with(componentBuilder.build())),
308                    node("//component/@alias", nullValue()));
309     }
310 
311     @Test
312     public void componentHasSpecifiedAlias() throws Exception
313     {
314         componentBuilder.alias(some("my-alias"));
315         
316         assertThat(applyChanges(changeset().with(componentBuilder.build())),
317                    node("//component/@alias", nodeTextEquals("my-alias")));
318     }
319 
320     @Test
321     public void componentHasNoDescriptionByDefault() throws Exception
322     {
323         assertThat(applyChanges(changeset().with(componentBuilder.build())),
324                    node("//component/description", nullValue()));
325     }
326 
327     @Test
328     public void componentHasSpecifiedDescription() throws Exception
329     {
330         componentBuilder.description(some("desc"));
331         
332         assertThat(applyChanges(changeset().with(componentBuilder.build())),
333                    node("//component/description", nodeTextEquals("desc")));
334     }
335 
336     @Test
337     public void componentHasNoDescriptionI18nKeyByDefault() throws Exception
338     {
339         componentBuilder.description(some("desc"));
340         
341         assertThat(applyChanges(changeset().with(componentBuilder.build())),
342                    node("//component/description/@key", nullValue()));
343     }
344 
345     @Test
346     public void componentHasSpecifiedDescriptionI18nKey() throws Exception
347     {
348         componentBuilder.description(some("desc")).descriptionI18nKey(some("desc-key"));
349         
350         assertThat(applyChanges(changeset().with(componentBuilder.build())),
351                    node("//component/description/@key", nodeTextEquals("desc-key")));
352     }
353 
354     @Test
355     public void componentHasNoInterfaceByDefault() throws Exception
356     {
357         assertThat(applyChanges(changeset().with(componentBuilder.build())),
358                    node("//component/interface", nullValue()));
359     }
360 
361     @Test
362     public void componentHasSpecifiedInterface() throws Exception
363     {
364         componentBuilder.interfaceId(some(fullyQualified(INTERFACE)));
365         
366         assertThat(applyChanges(changeset().with(componentBuilder.build())),
367                    node("//component/interface", nodeTextEquals(INTERFACE)));
368     }
369 
370     @Test
371     public void componentHasNoServicePropertiesByDefault() throws Exception
372     {
373         assertThat(applyChanges(changeset().with(componentBuilder.build())),
374                    node("//component/service-properties", nullValue()));
375     }
376 
377     @Test
378     public void componentHasSpecifiedServiceProperties() throws Exception
379     {
380         componentBuilder.serviceProperties(ImmutableMap.of("foo", "bar"));
381         
382         assertThat(applyChanges(changeset().with(componentBuilder.build())),
383                    node("//component/service-properties/entry[@key='foo']/@value", nodeTextEquals("bar")));
384     }
385 
386     @Test
387     public void duplicateComponentKeyCannotBeAdded() throws Exception
388     {
389         ComponentDeclaration firstComponent = componentBuilder.name(some("first")).build();
390         ComponentDeclaration secondComponentWithSameKey = componentBuilder.name(some("different")).build();
391         applyChanges(changeset().with(firstComponent));
392         
393         assertThat(applyChanges(changeset().with(secondComponentWithSameKey)),
394                    nodes("//component", nodeCount(1)));
395     }
396 
397     @Test
398     public void componentWithSameKeyIsNotOverwritten() throws Exception
399     {
400         ComponentDeclaration firstComponent = componentBuilder.name(some("first")).build();
401         ComponentDeclaration secondComponentWithSameKey = componentBuilder.name(some("different")).build();
402         applyChanges(changeset().with(firstComponent));
403         
404         assertThat(applyChanges(changeset().with(secondComponentWithSameKey)),
405                    node("//component/@name", nodeTextEquals("first")));
406     }
407     
408     @Test
409     public void moduleDescriptorIsAdded() throws Exception
410     {
411         String module = "<my-module>has some content</my-module>";
412         assertThat(applyChanges(changeset().with(moduleDescriptor(module))),
413                    node("//my-module", nodeTextEquals("has some content")));
414     }
415     
416     @Test
417     public void moduleDescriptorIsPlacedAfterOtherModuleDescriptorsOfSameElementName() throws Exception
418     {
419         String module1 = "<foo-module type=\"test\">has some content</foo-module>";
420         String module2 = "<bar-module type=\"test\">has some content</bar-module>";
421         applyChanges(changeset().with(moduleDescriptor(module1), moduleDescriptor(module2)));
422         
423         String module3 = "<foo-module type=\"test\">another one</foo-module>";
424         assertThat(applyChanges(changeset().with(moduleDescriptor(module3))),
425                    node("//*[@type='test'][2]", nodeName(equalTo("bar-module"))));
426     }
427     
428     @Test
429     public void moduleDescriptorWithSameTypeAndKeyCannotBeAdded() throws Exception
430     {
431         String module1 = "<my-module key=\"my-key\">good</my-module>";
432         String module2 = "<my-module key=\"my-key\">worse</my-module>";
433         applyChanges(changeset().with(moduleDescriptor(module1)));
434         
435         assertThat(applyChanges(changeset().with(moduleDescriptor(module2))),
436                    nodes("//my-module", nodeCount(1)));
437     }
438 
439     @Test
440     public void moduleDescriptorWithSameTypeAndKeyIsNotOverwritten() throws Exception
441     {
442         String module1 = "<my-module key=\"my-key\">good</my-module>";
443         String module2 = "<my-module key=\"my-key\">worse</my-module>";
444         applyChanges(changeset().with(moduleDescriptor(module1)));
445         
446         assertThat(applyChanges(changeset().with(moduleDescriptor(module2))),
447                    node("//my-module", nodeTextEquals("good")));
448     }
449     
450     protected PluginProjectChangeset addPluginParam()
451     {
452         return new PluginProjectChangeset().with(pluginParameter("foo", "bar"));
453     }
454     
455     protected PluginProjectChangeset addI18nProperty()
456     {
457         return new PluginProjectChangeset().with(i18nString("foo", "bar"));
458     }
459     
460     protected XmlWrapper applyChanges(PluginProjectChangeset changes) throws Exception
461     {
462         rewriter.applyChanges(changes);
463         return XmlMatchers.xml(FileUtils.readFileToString(helper.pluginXml));
464     }
465 }