1. WorkbenchTest.java
1.1. Open Modules
@Test
void openModule() {
robot.interact(() -> {
// Open first
workbench.openModule(first);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(1, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first);
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Open last
workbench.openModule(last);
assertSame(last, workbench.getActiveModule());
assertSame(moduleNodes[LAST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
inOrder = inOrder(first, last);
inOrder.verify(first).deactivate();
inOrder.verify(last).init(workbench);
inOrder.verify(last).activate();
// Open last again
workbench.openModule(last);
assertSame(last, workbench.getActiveModule());
assertSame(moduleNodes[LAST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
verify(last, times(1)).init(workbench);
verify(last, times(1)).activate();
verify(last, never()).deactivate();
// Open first (already initialized)
workbench.openModule(first);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
verify(first, times(1)).init(workbench); // no additional init on first
verify(last, times(1)).init(workbench); // no additional init on last
inOrder = inOrder(first, last);
inOrder.verify(last).deactivate();
inOrder.verify(first).activate();
verify(first, times(2)).activate();
// Switch to home screen
workbench.openAddModulePage();
assertSame(null, workbench.getActiveModule());
assertSame(null, workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
verify(first, times(1)).init(workbench); // no additional init on first
verify(last, times(1)).init(workbench); // no additional init on last
verify(first, times(2)).deactivate();
// Open second
workbench.openModule(second);
assertSame(second, workbench.getActiveModule());
assertSame(moduleNodes[SECOND_INDEX], workbench.getActiveModuleView());
assertEquals(3, workbench.getOpenModules().size());
inOrder = inOrder(second);
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
ignoreModuleGetters(first, second, last);
verifyNoMoreInteractions(first, second, last);
});
}
@Test
void openModuleInvalid() {
/* Test if opening a module which has not been passed in the constructor of WorkbenchFxModel
throws an exception */
robot.interact(() -> {
assertThrows(IllegalArgumentException.class,
() -> workbench.openModule(mock(WorkbenchModule.class)));
});
}
1.2. Close Modules
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModuleOne() {
// open and close module
robot.interact(() -> {
workbench.openModule(first);
workbench.closeModule(first);
assertSame(null, workbench.getActiveModule());
assertSame(null, workbench.getActiveModuleView());
assertEquals(0, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.closeModule(first)
inOrder.verify(first).deactivate();
inOrder.verify(first).destroy();
ignoreModuleGetters(first);
verifyNoMoreInteractions(first);
});
}
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModuleLeft1() {
robot.interact(() -> {
// open two modules, close left module
// right active
workbench.openModule(first);
workbench.openModule(second);
workbench.closeModule(first);
assertSame(second, workbench.getActiveModule());
assertSame(moduleNodes[SECOND_INDEX], workbench.getActiveModuleView());
assertEquals(1, workbench.getOpenModules().size());
verify(second, never()).deactivate();
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.closeModule(first)
inOrder.verify(first, never()).deactivate();
inOrder.verify(first).destroy();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModuleLeft2() {
robot.interact(() -> {
// open two modules, close left module
// left active
workbench.openModule(first);
workbench.openModule(second);
workbench.openModule(first);
workbench.closeModule(first);
assertSame(second, workbench.getActiveModule());
assertSame(moduleNodes[SECOND_INDEX], workbench.getActiveModuleView());
assertEquals(1, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.openModule(first)
inOrder.verify(second).deactivate();
inOrder.verify(first).activate();
// Call: workbench.closeModule(first)
inOrder.verify(first).deactivate();
inOrder.verify(first).destroy();
inOrder.verify(second).activate();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModuleRight1() {
// open two modules, close right module
// right active
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.closeModule(second);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(1, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.closeModule(second)
inOrder.verify(second).deactivate();
inOrder.verify(second).destroy();
inOrder.verify(first).activate();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModuleRight2() {
// open two modules, close right module
// left active
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.openModule(first);
workbench.closeModule(second);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(1, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.openModule(first)
inOrder.verify(second).deactivate();
inOrder.verify(first).activate();
// Call: workbench.closeModule(second)
inOrder.verify(second, never()).deactivate();
inOrder.verify(second).destroy();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModuleMiddleActive() {
// open three modules and close middle module
// middle active
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.openModule(last);
workbench.openModule(second);
workbench.closeModule(second);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second, last);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.openModule(last)
inOrder.verify(second).deactivate();
inOrder.verify(last).init(workbench);
inOrder.verify(last).activate();
// Call: workbench.openModule(second)
inOrder.verify(last).deactivate();
inOrder.verify(second).activate();
// Call: workbench.closeModule(second)
inOrder.verify(second).deactivate();
inOrder.verify(second).destroy();
inOrder.verify(first).activate();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
1.3. Close Modules Interrupted
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModulePreventDestroyActive() {
// open two modules, close second (active) module
// destroy() on second module will return false, so the module shouldn't get closed
when(second.destroy()).thenReturn(false);
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.closeModule(second);
assertSame(second, workbench.getActiveModule());
assertSame(moduleNodes[SECOND_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.closeModule(second)
// destroy second
inOrder.verify(second).deactivate();
inOrder.verify(second).destroy();
// notice destroy() was unsuccessful, keep focus on second
inOrder.verify(second).activate();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Precondition: openModule tests pass.
*/
@Test
void closeModulePreventDestroyInactive() {
// open two modules, close first (inactive) module
// destroy() on first module will return false, so the module shouldn't get closed
when(first.destroy()).thenReturn(false);
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.closeModule(first);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.closeModule(second)
// destroy second
inOrder.verify(first, never()).deactivate();
inOrder.verify(first).destroy();
// notice destroy() was unsuccessful, switch focus to first
inOrder.verify(second).deactivate();
inOrder.verify(first).activate();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Example of what happens in case of a closing dialog in the destroy() method of a module with
* the user confirming the module should get closed. Precondition: openModule tests pass.
*/
@Test
void closeModuleDestroyInactiveDialogClose() {
// open two modules, close first (inactive) module
// destroy() on first module will return false, so the module shouldn't get closed
when(first.destroy()).then(invocation -> {
// dialog opens
return false;
});
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.closeModule(first);
// user confirms yes on dialog: WorkbenchModule#close()
simulateModuleClose(first);
assertSame(second, workbench.getActiveModule());
assertSame(moduleNodes[SECOND_INDEX], workbench.getActiveModuleView());
assertEquals(1, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.closeModule(first)
// attempt to destroy first
inOrder.verify(first).destroy();
// destroy() returns false, closeModule() opens first module
inOrder.verify(second).deactivate();
inOrder.verify(first).activate();
// WorkbenchModule#close(), switch to second
inOrder.verify(first).deactivate();
inOrder.verify(second).activate();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
/**
* Internal testing utility method.
* Ignores calls to the getters of {@link WorkbenchModule}, which enables to safely call
* {@link Mockito#verifyNoMoreInteractions} during lifecycle order verification tests without
* having to make assumptions about how many times the getters have been called as well.
*/
private void ignoreModuleGetters(WorkbenchModule... modules) {
for (WorkbenchModule module : modules) {
verify(module, atLeast(0)).getIcon();
verify(module, atLeast(0)).getName();
verify(module, atLeast(0)).getWorkbench();
verify(module, atLeast(0)).getToolbarControlsLeft();
verify(module, atLeast(0)).getToolbarControlsRight();
}
}
/**
* Internal testing method which simulates a call to {@link WorkbenchModule#close()}.
*/
private void simulateModuleClose(WorkbenchModule module) {
workbench.completeModuleCloseable(module);
}
/**
* Example of what happens in case of a closing dialog in the destroy() method of a module with
* the user confirming the module should NOT get closed. Precondition: openModule tests pass.
*/
@Test
void closeModulePreventDestroyInactiveDialogClose() {
// open two modules, close first (inactive) module
// destroy() on first module will return false, so the module shouldn't get closed
when(first.destroy()).then(invocation -> {
// dialog opens, user confirms NOT closing module
return false;
});
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.closeModule(first);
assertSame(first, workbench.getActiveModule());
assertSame(moduleNodes[FIRST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.closeModule(first)
// attempt to destroy first
inOrder.verify(first, never()).deactivate();
inOrder.verify(first).destroy();
// destroy() returns false, switch open module to first
inOrder.verify(second).deactivate();
inOrder.verify(first).activate();
// destroy() returns false, first stays the active module
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
});
}
@Test
void closeModuleInvalid() {
robot.interact(() -> {
// Test for null
assertThrows(NullPointerException.class, () -> workbench.closeModule(null));
// Test if closing a module not included in the modules at all throws an exception
assertThrows(IllegalArgumentException.class,
() -> workbench.closeModule(mock(WorkbenchModule.class)));
// Test if closing a module not opened throws an exception
assertThrows(IllegalArgumentException.class, () -> workbench.closeModule(mockModules[0]));
});
}
@Test
void closeInactiveModule() {
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
workbench.openModule(last);
workbench.closeModule(second);
assertSame(last, workbench.getActiveModule());
assertSame(moduleNodes[LAST_INDEX], workbench.getActiveModuleView());
assertEquals(2, workbench.getOpenModules().size());
InOrder inOrder = inOrder(first, second, last);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Call: workbench.openModule(last)
inOrder.verify(second).deactivate();
inOrder.verify(last).init(workbench);
inOrder.verify(last).activate();
// Call: workbench.closeModule(second)
inOrder.verify(second, never()).deactivate();
inOrder.verify(second).destroy();
inOrder.verify(last).getWorkbench();
inOrder.verify(last).getName();
inOrder.verify(last).getIcon();
ignoreModuleGetters(first, second, last);
verifyNoMoreInteractions(first, second, last);
});
}
1.4. Close Stage Interrupted
/**
* Test for {@link Workbench#setupCleanup()}.
* Simulates all modules returning {@code true} when
* {@link WorkbenchModule#destroy()} is being called on them during the cleanup.
*/
@Test
void closeStageSuccess() {
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
// simulate closing of the stage by pressing the X of the application
closeStage();
// all open modules should get closed before the application ends
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Effects caused by "Workbench#setupCleanup" -> setOnCloseRequest
// Implicit Call: workbench.closeModule(first)
inOrder.verify(first, never()).deactivate();
inOrder.verify(first).destroy();
// Implicit Call: workbench.closeModule(second)
inOrder.verify(second).deactivate();
inOrder.verify(second).destroy();
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
assertEquals(0, workbench.getOpenModules().size());
});
}
/**
* Test for {@link Workbench#setupCleanup()}.
* Simulates the first (inactive) module returning {@code false} and the second (active) module
* returning {@code true}, when {@link WorkbenchModule#destroy()} is being called
* on them during cleanup.
*/
@Test
void closeStageFailFirstModule() {
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
// make sure closing of the stage gets interrupted, if destroy returns false on a module
when(first.destroy()).thenReturn(false);
// simulate closing of the stage like when pressing the X of the application
closeStage();
// all open modules should get closed before the application ends
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Effects caused by "Workbench#setupCleanup" -> setOnCloseRequest
// Implicit Call: workbench.closeModule(first)
inOrder.verify(first, never()).deactivate();
inOrder.verify(first).destroy(); // returns false
// Implicit Call: workbench.openModule(first) -> set focus on module that couldn't be closed
inOrder.verify(second).deactivate();
inOrder.verify(first).activate();
// closing should be interrupted
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
assertEquals(2, workbench.getOpenModules().size());
});
}
/**
* Test for {@link Workbench#setupCleanup()}.
* Simulates the first (inactive) module returning {@code true} and the second (active) module
* returning {@code false}, when {@link WorkbenchModule#destroy()} is being called on them during
* cleanup.
*/
@Test
void closeStageFailSecondModule() {
robot.interact(() -> {
workbench.openModule(first);
workbench.openModule(second);
// make sure closing of the stage gets interrupted, if destroy returns false on a module
when(second.destroy()).thenReturn(false);
// simulate closing of the stage by pressing the X of the application
closeStage();
// all open modules should get closed before the application ends
InOrder inOrder = inOrder(first, second);
// Call: workbench.openModule(first)
inOrder.verify(first).init(workbench);
inOrder.verify(first).activate();
// Call: workbench.openModule(second)
inOrder.verify(first).deactivate();
inOrder.verify(second).init(workbench);
inOrder.verify(second).activate();
// Effects caused by "Workbench#setupCleanup" -> setOnCloseRequest
// Implicit Call: workbench.closeModule(first)
inOrder.verify(first, never()).deactivate();
inOrder.verify(first).destroy(); // returns true
// Implicit Call: workbench.closeModule(second)
inOrder.verify(second).deactivate();
inOrder.verify(second).destroy(); // returns false
// second should stay as the active module
inOrder.verify(second).activate();
// closing should be interrupted
ignoreModuleGetters(first, second);
verifyNoMoreInteractions(first, second);
assertEquals(1, workbench.getOpenModules().size());
assertEquals(second, workbench.getOpenModules().get(0));
});
}
/**
* Test for {@link Workbench#setupCleanup()}.
* Simulates a special case that caused in bug scenarios to have 2x {@code thenRun} set on
* {@code moduleCloseable} in {@code stage.setOnCloseRequest}, which lead to 2 dialogs being open
* instead of one, after the first module has been closed.
*/
@Test
void closeStageSpecial1() {
robot.interact(() -> {
// Given: 2 Modules open, destroy() on both opens a dialog and returns false.
// Pressing yes on the dialog calls WorkbenchModule#close(), pressing no leaves the
// module open.
workbench.openModule(first);
workbench.openModule(second);
when(first.destroy()).then(invocationOnMock -> {
workbench.showDialog(WorkbenchDialog.builder("1", "", WorkbenchDialog.Type.CONFIRMATION)
.blocking(true).onResult(buttonType -> {
if (ButtonType.YES.equals(buttonType)) {
simulateModuleClose(first);
}
}).build());
return false;
});
when(second.destroy()).then(invocationOnMock -> {
workbench.showDialog(WorkbenchDialog.builder("2", "", WorkbenchDialog.Type.CONFIRMATION)
.blocking(true).onResult(buttonType -> {
if (ButtonType.YES.equals(buttonType)) {
simulateModuleClose(second);
}
}).build());
return false;
});
assertTrue(isStageOpen());
// When: Close stage, press No, Close Stage, press yes.
closeStage();
assertSame(1, workbench.getBlockingOverlaysShown().size());
assertSame(2, workbench.getOpenModules().size());
simulateDialogButtonClick(ButtonType.NO);
assertSame(0, workbench.getBlockingOverlaysShown().size());
assertSame(2, workbench.getOpenModules().size());
closeStage();
assertSame(1, workbench.getBlockingOverlaysShown().size());
assertSame(2, workbench.getOpenModules().size());
simulateDialogButtonClick(ButtonType.YES);
assertSame(1, workbench.getBlockingOverlaysShown().size());
assertSame(1, workbench.getOpenModules().size());
// Then: Only second module is open and 1 dialog is open (closing of second module)
assertEquals(second, workbench.getOpenModules().get(0));
assertEquals("2", getShowingDialogControl().getDialog().getTitle());
// When: Press yes
simulateDialogButtonClick(ButtonType.YES);
// Then: No modules and dialogs are open, stage is closed.
assertSame(0, workbench.getBlockingOverlaysShown().size());
assertSame(0, workbench.getOpenModules().size());
assertFalse(isStageOpen());
});
}
/**
* Test for {@link Workbench#setupCleanup()}.
* Simulates a special case that caused in bug scenarios for the stage closing process to go on,
* even though a tab was closed and not the stage itself.
*/
@Test
void closeStageSpecial2() {
robot.interact(() -> {
// Given: 2 Modules open, destroy() on both opens a dialog and returns false.
// Pressing yes on the dialog calls WorkbenchModule#close(), pressing no leaves the
// module open.
workbench.openModule(first);
workbench.openModule(second);
when(first.destroy()).then(invocationOnMock -> {
workbench.showDialog(WorkbenchDialog.builder("1", "", WorkbenchDialog.Type.CONFIRMATION)
.blocking(true).onResult(buttonType -> {
if (ButtonType.YES.equals(buttonType)) {
simulateModuleClose(first);
}
}).build());
return false;
});
when(second.destroy()).then(invocationOnMock -> {
workbench.showDialog(WorkbenchDialog.builder("2", "", WorkbenchDialog.Type.CONFIRMATION)
.blocking(true).onResult(buttonType -> {
if (ButtonType.YES.equals(buttonType)) {
simulateModuleClose(second);
}
}).build());
return false;
});
assertTrue(isStageOpen());
// When: Close stage, press No, Close Tab, Press Yes
closeStage();
assertSame(1, workbench.getBlockingOverlaysShown().size());
assertSame(2, workbench.getOpenModules().size());
simulateDialogButtonClick(ButtonType.NO);
assertSame(0, workbench.getBlockingOverlaysShown().size());
assertSame(2, workbench.getOpenModules().size());
workbench.closeModule(first); // simulate tab closing
assertSame(1, workbench.getBlockingOverlaysShown().size());
assertSame(2, workbench.getOpenModules().size());
simulateDialogButtonClick(ButtonType.YES);
assertSame(0, workbench.getBlockingOverlaysShown().size());
assertSame(1, workbench.getOpenModules().size());
// Then: Only second module is open and no dialogs are open (stage closing is interrupted)
assertEquals(second, workbench.getOpenModules().get(0));
assertTrue(isStageOpen());
});
}
/**
* Internal utility method for testing.
* Determines whether the current stage is open or was closed.
*/
private boolean isStageOpen() {
return robot.listTargetWindows().size() == 1;
}
/**
* Internal utility method for testing.
* Simulates closing the stage, which fires a close request to test logic
* inside of {@link Stage#setOnCloseRequest(EventHandler)}.
* Using {@link FxRobot#closeCurrentWindow()} would be better, but it only works on Windows
* because of its implementation, so this approach was chosen as a workaround.
* @see <a href="https://github.com/TestFX/TestFX/issues/447">
* closeCurrentWindow() doesn't work headless</a>
*/
private void closeStage() {
Stage stage = ((Stage) workbench.getScene().getWindow());
stage.fireEvent(
new WindowEvent(
stage,
WindowEvent.WINDOW_CLOSE_REQUEST
)
);
}