IDEA-78280 (Grails: 'Evaluate Expression' broken after controller reload)
[idea/community.git] / plugins / git4idea / tests / git4idea / branch / GitBranchOperationsTest.java
1 package git4idea.branch;
2
3 import com.intellij.notification.Notification;
4 import com.intellij.notification.NotificationType;
5 import com.intellij.openapi.progress.EmptyProgressIndicator;
6 import com.intellij.openapi.progress.ProgressIndicator;
7 import com.intellij.openapi.ui.DialogWrapper;
8 import com.intellij.openapi.ui.Messages;
9 import com.intellij.openapi.util.text.StringUtil;
10 import com.intellij.openapi.vcs.ProjectLevelVcsManager;
11 import com.intellij.openapi.vcs.VcsConfiguration;
12 import com.intellij.openapi.vcs.VcsShowConfirmationOption;
13 import com.intellij.openapi.vfs.VirtualFile;
14 import com.intellij.testFramework.AbstractVcsTestCase;
15 import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
16 import com.intellij.testFramework.fixtures.TempDirTestFixture;
17 import com.intellij.util.ui.UIUtil;
18 import com.intellij.vcsUtil.VcsUtil;
19 import git4idea.GitBranch;
20 import git4idea.GitVcs;
21 import git4idea.repo.GitRepository;
22 import git4idea.test.GitTestScenarioGenerator;
23 import git4idea.test.GitTestUtil;
24 import git4idea.test.TestMessageManager;
25 import git4idea.test.TestNotificationManager;
26 import git4idea.tests.TestDialogHandler;
27 import git4idea.tests.TestDialogManager;
28 import git4idea.util.UntrackedFilesNotifier;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 import org.testng.annotations.AfterMethod;
32 import org.testng.annotations.BeforeMethod;
33 import org.testng.annotations.Test;
34
35 import java.io.File;
36 import java.io.IOException;
37 import java.lang.reflect.InvocationTargetException;
38 import java.lang.reflect.Method;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.concurrent.atomic.AtomicBoolean;
42
43 import static git4idea.test.GitExec.*;
44 import static org.testng.Assert.*;
45
46 /**
47  * @author Kirill Likhodedov
48  */
49 public class GitBranchOperationsTest extends AbstractVcsTestCase  {
50
51   private static final String NEW_BRANCH = "new_branch";
52   private static final String MASTER = "master";
53
54   private Collection<GitRepository> myRepositories;
55   private GitRepository myUltimate;
56   private GitRepository myCommunity;
57   private GitRepository myContrib;
58
59   private TestMessageManager myMessageManager;
60   private TestNotificationManager myNotificationManager;
61   private TestDialogManager myDialogManager;
62   
63   private TempDirTestFixture myTempDirFixture;
64
65   @BeforeMethod
66   public void setup() throws Exception {
67     final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory();
68     myTempDirFixture = fixtureFactory.createTempDirTestFixture();
69     myTempDirFixture.setUp();
70     
71     final File projectDir = new File(myTempDirFixture.getTempDirPath(), "ultimate");
72     assertTrue(projectDir.mkdir());
73     
74     UIUtil.invokeAndWaitIfNeeded(new Runnable() {
75       @Override
76       public void run() {
77         try {
78           initProject(projectDir);
79           initRepositories(VcsUtil.getVirtualFile(projectDir));
80         }
81         catch (Exception e) {
82           throw new RuntimeException("Exception initializing the test", e);
83         }
84       }
85     });
86
87     GitVcs vcs = GitVcs.getInstance(myProject);
88     assertNotNull(vcs);
89     myTraceClient = true;
90     doActionSilently(VcsConfiguration.StandardConfirmation.ADD);
91     doActionSilently(VcsConfiguration.StandardConfirmation.REMOVE);
92
93     myDialogManager = GitTestUtil.registerDialogManager(myProject);
94     myNotificationManager = GitTestUtil.registerNotificationManager(myProject);
95     myMessageManager = GitTestUtil.registerMessageManager(myProject);
96     
97     createAddCommit(myUltimate, "a");
98     createAddCommit(myCommunity, "a");
99     createAddCommit(myContrib, "a");
100
101     myUltimate.getRoot().refresh(false, true);
102   }
103   
104   protected void doActionSilently(final VcsConfiguration.StandardConfirmation op) {
105     setStandardConfirmation(GitVcs.NAME, op, VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY);
106   }
107   
108   @AfterMethod
109   public void tearDown() throws Exception {
110     if (myTempDirFixture != null) {
111       myTempDirFixture.tearDown();
112       myTempDirFixture = null;
113     }
114   }
115
116   private void initRepositories(VirtualFile projectDir) throws IOException {
117     myUltimate = init(myProject, projectDir);
118     VirtualFile communityDir = createDirInCommand(projectDir, "community");
119     VirtualFile contribDir = createDirInCommand(projectDir, "contrib");
120     myCommunity = init(myProject, communityDir);
121     myContrib = init(myProject, contribDir);
122
123     addProjectRoots();
124     myRepositories = Arrays.asList(myUltimate, myCommunity, myContrib);
125   }
126
127   private void addProjectRoots() {
128     ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
129     vcsManager.setDirectoryMapping(myUltimate.getRoot().getPath(), GitVcs.NAME);
130     vcsManager.setDirectoryMapping(myCommunity.getRoot().getPath(), GitVcs.NAME);
131     vcsManager.setDirectoryMapping(myContrib.getRoot().getPath(), GitVcs.NAME);
132   }
133
134   @Test
135   public void create_new_branch_without_problems() throws Exception {
136     doCheckoutNewBranch();
137     assertNotify(NotificationType.INFORMATION, "Branch new_branch was created");
138   }
139
140   @Test
141   public void create_new_branch_with_unmerged_files_in_first_repo_should_show_notification() throws Exception {
142     GitTestScenarioGenerator.prepareUnmergedFiles(myUltimate);
143     doCheckoutNewBranch();
144     assertNotify(NotificationType.ERROR, GitBranchOperation.UNMERGED_FILES_ERROR_NOTIFICATION_DESCRIPTION);
145   }
146   
147   @Test
148   public void create_new_branch_with_unmerged_files_in_second_repo_should_propose_to_rollback() throws Exception {
149     GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity, myContrib);
150     doCheckoutNewBranch();
151     assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
152   }
153
154   @Test
155   public void rollback_create_new_branch_should_delete_branch() throws Exception {
156     GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity, myContrib);
157     myMessageManager.nextAnswer(Messages.OK);
158     doCheckoutNewBranch();
159     assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
160     assertBranch("master");
161     assertTrue(!branch(myUltimate).contains(NEW_BRANCH));
162   }
163
164   @Test
165   public void deny_rollback_create_new_branch() throws Exception {
166     GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity, myContrib);
167     myMessageManager.nextAnswer(Messages.CANCEL);
168     doCheckoutNewBranch();
169     assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
170
171     assertBranch(myUltimate, NEW_BRANCH);
172     assertBranch(myCommunity, MASTER);
173     assertBranch(myContrib, MASTER);
174   }
175   
176   @Test
177   public void checkout_without_problems() throws Exception {
178     prepareBranchForSimpleCheckout();
179     doCheckout("feature", null);
180     assertNotify(NotificationType.INFORMATION, "Checked out feature");
181   }
182   
183   @Test
184   public void checkout_with_unmerged_files_in_first_repo_should_show_notification() throws Exception {
185     prepareBranchForSimpleCheckout();
186     GitTestScenarioGenerator.prepareUnmergedFiles(myUltimate);
187     doCheckout("feature", null);
188     assertNotify(NotificationType.ERROR, GitBranchOperation.UNMERGED_FILES_ERROR_NOTIFICATION_DESCRIPTION);
189   }
190   
191   @Test
192   public void checkout_with_unmerged_file_in_second_repo_should_propose_to_rollback() throws Exception {
193     prepareBranchForSimpleCheckout();
194     GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity);
195     doCheckout("feature", null);
196     assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
197   }
198
199   @Test
200   public void rollback_checkout_should_return_to_previous_branch() throws Exception {
201     prepareBranchForSimpleCheckout();
202     GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity);
203     myMessageManager.nextAnswer(Messages.OK);
204     doCheckout("feature", null);
205     assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
206     assertBranch("master");
207   }
208
209   @Test
210   public void deny_rollback_checkout_should_do_nothing() throws Exception {
211     prepareBranchForSimpleCheckout();
212     GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity);
213     myMessageManager.nextAnswer(Messages.CANCEL);
214     doCheckout("feature", null);
215     assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
216     assertBranch(myUltimate, "feature");
217     assertBranch(myCommunity, "master");
218     assertBranch(myContrib, "master");
219   }
220
221   @Test
222   public void checkout_with_untracked_files_overwritten_by_checkout_in_first_repo_should_show_notification() throws Exception {
223     prepareUntrackedFilesAndBranchWithSameTrackedFiles(myUltimate);
224     branch(myCommunity, "feature");
225     branch(myContrib, "feature");
226
227     doCheckout("feature", null);
228     assertNotify(NotificationType.ERROR, UntrackedFilesNotifier.createUntrackedFilesOverwrittenDescription("checkout", false));
229   }
230
231   @Test
232   public void checkout_with_untracked_files_overwritten_by_checkout_in_second_repo_should_show_rollback_proposal_with_file_list() throws Exception {
233     prepareUntrackedFilesAndBranchWithSameTrackedFiles(myCommunity);
234     branch(myUltimate, "feature");
235     branch(myContrib, "feature");
236
237     Class gitCheckoutOperationClass = Class.forName("git4idea.branch.GitCheckoutOperation");
238     Class[] classes = gitCheckoutOperationClass.getDeclaredClasses();
239     Class untrackedFilesDialogClass = null;
240     for (Class aClass : classes) {
241       if (aClass.getName().endsWith("UntrackedFilesDialog")) {
242         untrackedFilesDialogClass = aClass;
243       }
244     }
245     assertNotNull(untrackedFilesDialogClass);
246     
247     final AtomicBoolean dialogShown = new AtomicBoolean();
248     final Class finalUntrackedFilesDialogClass = untrackedFilesDialogClass;
249     myDialogManager.registerDialogHandler(untrackedFilesDialogClass, new TestDialogHandler() {
250       @Override
251       public int handleDialog(Object dialog) {
252         if (dialog.getClass().equals(finalUntrackedFilesDialogClass)) {
253           dialogShown.set(true);
254         }
255         return DialogWrapper.CANCEL_EXIT_CODE;
256       }
257     });
258
259     doCheckout("feature", null);
260     assertTrue(dialogShown.get());
261   }
262
263   @Test
264   public void checkout_with_local_changes_overwritten_by_checkout_should_show_smart_checkout_dialog() throws Exception {
265     prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myUltimate);
266     branch(myCommunity, "feature");
267     branch(myContrib, "feature");
268
269     final AtomicBoolean dialogShown = new AtomicBoolean();
270     myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class, new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
271       @Override
272       public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
273         dialogShown.set(true);
274         return DialogWrapper.CANCEL_EXIT_CODE;
275       }
276     });
277
278     doCheckout("feature", null);
279     assertTrue(dialogShown.get());
280   }
281
282   @Test
283   public void agree_to_smart_checkout_should_smart_checkout() throws Exception {
284     prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myUltimate);
285     prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myCommunity);
286     prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myContrib);
287     myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class,
288       new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
289         @Override
290         public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
291           return DialogWrapper.OK_EXIT_CODE;
292         }
293     });
294
295     doCheckout("feature", null);
296     assertBranch("feature");
297     for (GitRepository repository : myRepositories) {
298       refresh(repository);
299       assertBranch(repository, "feature");
300       assertEquals(read(repository, "local.txt"), "master\ninitial content\nfeature content\n");
301     }
302   }
303
304   @Test
305   public void deny_to_smart_checkout_in_first_repo_should_show_notification() throws Exception {
306     prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myUltimate);
307     branch(myCommunity, "feature");
308     branch(myContrib, "feature");
309
310     myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class, new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
311       @Override
312       public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
313         return DialogWrapper.CANCEL_EXIT_CODE;
314       }
315     });
316
317     doCheckout("feature", null);
318     assertNotify(NotificationType.ERROR, "Couldn't checkout feature", stripHtmlAndBreaks("Local changes would be overwritten by checkout." +
319                                                                                   "Stash or commit them before checking out a branch."));
320     assertBranch("master");
321   }
322
323   @Test
324   public void deny_to_smart_checkout_in_second_repo_should_show_rollback_proposal() throws Exception {
325     prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myCommunity);
326     branch(myUltimate, "feature");
327     branch(myContrib, "feature");
328     myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class, new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
329       @Override
330       public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
331         return DialogWrapper.CANCEL_EXIT_CODE;
332       }
333     });
334
335     doCheckout("feature", null);
336     assertMessage("Couldn't checkout feature", 
337                   "Local changes would be overwritten by checkout.<br/>Stash or commit them before checking out a branch.<br/>" + 
338                   "However checkout has succeeded for the following repositories:<br/>" +
339                    myUltimate.getPresentableUrl() +
340                    "<br/>You may rollback (checkout back to master) not to let branches diverge.",
341                   "Rollback", "Don't rollback");
342   }
343
344   private static void prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(GitRepository repository) throws IOException {
345     create(repository, "local.txt", "initial content\n");
346     addCommit(repository);
347     checkout(repository, "-b", "feature");
348     edit(repository, "local.txt", "initial content\nfeature content\n");
349     addCommit(repository);
350     checkout(repository, "master");
351     edit(repository, "local.txt", "master\ninitial content\n");
352   }
353
354   private static void prepareUntrackedFilesAndBranchWithSameTrackedFiles(GitRepository repository) throws IOException {
355     checkout(repository, "-b", "feature");
356     createAddCommit(repository, "untracked.txt");
357     checkout(repository, "master");
358     create(repository, "untracked.txt");
359   }
360
361   private void prepareBranchForSimpleCheckout() throws IOException {
362     for (GitRepository repository : myRepositories) {
363       checkout(repository, "-b", "feature");
364       createAddCommit(repository, "feature_file.txt");
365       checkout(repository, "master");
366     }
367   }
368
369   @Test
370   public void delete_branch_without_problems() throws Exception {
371     for (GitRepository repository : myRepositories) {
372       branch(repository, "master1");
373       refresh(repository);
374     }
375     doDeleteBranch("master1");
376     assertNotify(NotificationType.INFORMATION, "Deleted branch master1");
377   }
378   
379   @Test
380   public void delete_unmerged_branch_should_show_dialog() throws Exception {
381     prepareUnmergedBranch(myUltimate, myCommunity, myContrib);
382
383     final AtomicBoolean dialogShown = new AtomicBoolean();
384     myDialogManager.registerDialogHandler(GitBranchIsNotFullyMergedDialog.class, new TestDialogHandler<GitBranchIsNotFullyMergedDialog>() {
385       @Override public int handleDialog(GitBranchIsNotFullyMergedDialog dialog) {
386         dialogShown.set(true);
387         return DialogWrapper.CANCEL_EXIT_CODE;
388       }
389     });
390
391     doDeleteBranch("unmerged_branch");
392     assertTrue(dialogShown.get());
393   }
394   
395   @Test
396   public void ok_in_unmerged_branch_dialog_should_force_delete_branch() throws Exception {
397     prepareUnmergedBranch(myUltimate, myCommunity, myContrib);
398     registerNotFullyMergedDialog(DialogWrapper.OK_EXIT_CODE);
399     doDeleteBranch("unmerged_branch");
400     for (GitRepository repository : myRepositories) {
401       assertTrue(!branch(repository).contains("unmerged_branch"));
402     }
403   }
404   
405   @Test
406   public void cancel_in_unmerged_branch_dialog_in_first_repository_should_show_notification() throws Exception {
407     prepareUnmergedBranch(myUltimate, myContrib);
408     branch(myCommunity, "unmerged_branch");
409
410     registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
411     doDeleteBranch("unmerged_branch");
412     assertNotify(NotificationType.ERROR, "Branch unmerged_branch wasn't deleted", "This branch is not fully merged to master");
413   }
414   
415   @Test
416   public void cancel_in_unmerged_branch_dialog_in_not_first_repository_should_show_rollback_proposal() throws Exception {
417     branch(myUltimate, "unmerged_branch");
418     prepareUnmergedBranch(myCommunity, myContrib);
419
420     registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
421     doDeleteBranch("unmerged_branch");
422     assertMessage(String.format("Branch %s wasn't deleted", "unmerged_branch"));
423   }
424   
425   @Test
426   public void rollback_delete_branch_should_recreate_branches() throws Exception {
427     branch(myUltimate, "unmerged_branch");
428     prepareUnmergedBranch(myCommunity);
429     branch(myContrib, "unmerged_branch");
430
431     registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
432     myMessageManager.nextAnswer(Messages.OK);
433     doDeleteBranch("unmerged_branch");
434
435     for (GitRepository repository : myRepositories) {
436       assertTrue(branch(repository).contains("unmerged_branch"));
437     }
438   }
439   
440   @Test
441   public void deny_rollback_delete_branch_should_do_nothing() throws Exception {
442     branch(myUltimate, "unmerged_branch");
443     prepareUnmergedBranch(myCommunity);
444     branch(myContrib, "unmerged_branch");
445     
446     registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
447     myMessageManager.nextAnswer(Messages.CANCEL);
448     doDeleteBranch("unmerged_branch");
449
450     assertTrue(branch(myCommunity).contains("unmerged_branch"));
451     assertTrue(branch(myContrib).contains("unmerged_branch"));
452     assertTrue(!branch(myUltimate).contains("unmerged_branch"));
453   }
454
455   private void registerNotFullyMergedDialog(final int answer) {
456     myDialogManager.registerDialogHandler(GitBranchIsNotFullyMergedDialog.class, new TestDialogHandler<GitBranchIsNotFullyMergedDialog>() {
457       @Override
458       public int handleDialog(GitBranchIsNotFullyMergedDialog dialog) {
459         return answer;
460       }
461     });
462   }
463
464   private static void prepareUnmergedBranch(GitRepository... repositories) throws IOException {
465     for (GitRepository repository : repositories) {
466       checkout(repository, "-b", "unmerged_branch");
467       createAddCommit(repository, "unmerged_branch_file");
468       checkout(repository, "master");
469       refresh(repository);
470     }
471   }
472
473   private void doCheckoutNewBranch() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
474     callPrivateBranchOperationsProcessorMethod("doCheckoutNewBranch", NEW_BRANCH);
475   }
476
477   private void callPrivateBranchOperationsProcessorMethod(String methodName, String branchName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
478     // call private doCheckoutNewBranch instead of public checkoutNewBranch to avoid dealing with background process creation
479     // same for other branch operations
480     GitBranchOperationsProcessor processor = new GitBranchOperationsProcessor(myProject, myRepositories, myCommunity);
481     Method method = GitBranchOperationsProcessor.class.getDeclaredMethod(methodName, String.class, ProgressIndicator.class);
482     method.setAccessible(true);
483     method.invoke(processor, branchName, new EmptyProgressIndicator());
484   }
485
486   private void doDeleteBranch(@NotNull String branchName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
487     callPrivateBranchOperationsProcessorMethod("doDelete", branchName);
488   }
489   
490   private void doCheckout(@NotNull String branchName, @Nullable String newBranch) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
491     GitBranchOperationsProcessor processor = new GitBranchOperationsProcessor(myProject, myRepositories, myCommunity);
492     Method doCheckout = GitBranchOperationsProcessor.class.getDeclaredMethod("doCheckout", ProgressIndicator.class, String.class, String.class);
493     doCheckout.setAccessible(true);
494     doCheckout.invoke(processor, new EmptyProgressIndicator(), branchName, newBranch);
495   }
496
497   private void assertBranch(String branch) {
498     for (GitRepository repository : myRepositories) {
499       assertBranch(repository, branch);
500     }
501   }
502
503   private static void assertBranch(GitRepository repository, String branchName) {
504     GitBranch currentBranch = repository.getCurrentBranch();
505     assertNotNull(currentBranch);
506     assertEquals(currentBranch.getName(), branchName);
507   }
508
509   private void assertNotify(NotificationType type, String content) {
510     assertNotify(type, null, content);
511   }
512
513   private void assertNotify(NotificationType type, @Nullable String title, String content) {
514     Notification notification = myNotificationManager.getLastNotification();
515     assertNotNull(notification);
516     assertEquals(stripHtmlAndBreaks(notification.getContent()), stripHtmlAndBreaks(content));
517     assertEquals(notification.getType(), type);
518     if (title != null) {
519       assertEquals(stripHtmlAndBreaks(notification.getTitle()), stripHtmlAndBreaks(title));
520     }
521   }
522
523   @NotNull
524   private static String stripHtmlAndBreaks(@NotNull String text) {
525     return StringUtil.stripHtml(text, true).replace("\n", "");
526   }
527
528   private void assertMessage(String title) {
529     assertMessage(title, null, null, null);
530   }
531   
532   private void assertMessage(@Nullable String title, @Nullable String description, @Nullable String yesButton, @Nullable String noButton) {
533     TestMessageManager.Message message = myMessageManager.getLastMessage();
534     assertNotNull(message);
535     if (title != null) {
536       assertEquals(message.getTitle(), title);
537     }
538     if (description != null) {
539       assertEquals(message.getDescription(), description);
540     }
541     if (yesButton != null) {
542       assertEquals(message.getYesText(), yesButton);
543     }
544     if (noButton != null) {
545       assertEquals(message.getNoText(), noButton);
546     }
547   }
548
549 }