Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more unit tests to three gui classes #7636

Merged
merged 11 commits into from
Apr 26, 2021
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ private MenuBar createMenu() {
factory.createMenuItem(StandardActions.COPY_KEY_AND_TITLE, new CopyMoreAction(StandardActions.COPY_KEY_AND_TITLE, dialogService, stateManager, Globals.getClipboardManager(), prefs)),
factory.createMenuItem(StandardActions.COPY_KEY_AND_LINK, new CopyMoreAction(StandardActions.COPY_KEY_AND_LINK, dialogService, stateManager, Globals.getClipboardManager(), prefs)),
factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, Globals.getClipboardManager(), prefs.getPreviewPreferences())),
factory.createMenuItem(StandardActions.EXPORT_SELECTED_TO_CLIPBOARD, new ExportToClipboardAction(this, dialogService, Globals.exportFactory, Globals.getClipboardManager(), Globals.TASK_EXECUTOR))),
factory.createMenuItem(StandardActions.EXPORT_SELECTED_TO_CLIPBOARD, new ExportToClipboardAction(this, dialogService, Globals.exportFactory, Globals.getClipboardManager(), Globals.TASK_EXECUTOR, prefs))),

factory.createMenuItem(StandardActions.PASTE, new EditAction(StandardActions.PASTE, this, stateManager)),

Expand Down
24 changes: 14 additions & 10 deletions src/main/java/org/jabref/gui/exporter/ExportToClipboardAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import org.jabref.gui.ClipBoardManager;
import org.jabref.gui.DialogService;
import org.jabref.gui.Globals;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.LibraryTab;
import org.jabref.gui.actions.SimpleCommand;
Expand All @@ -28,6 +27,7 @@
import org.jabref.logic.util.OS;
import org.jabref.logic.util.StandardFileType;
import org.jabref.model.entry.BibEntry;
import org.jabref.preferences.PreferencesService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -46,21 +46,25 @@ public class ExportToClipboardAction extends SimpleCommand {
private final ExporterFactory exporterFactory;
private final ClipBoardManager clipBoardManager;
private final TaskExecutor taskExecutor;
private final PreferencesService preferences;

public ExportToClipboardAction(JabRefFrame frame, DialogService dialogService, ExporterFactory exporterFactory, ClipBoardManager clipBoardManager, TaskExecutor taskExecutor) {
public ExportToClipboardAction(JabRefFrame frame, DialogService dialogService, ExporterFactory exporterFactory, ClipBoardManager clipBoardManager, TaskExecutor taskExecutor, PreferencesService prefs) {
this.frame = frame;
this.dialogService = dialogService;
this.exporterFactory = exporterFactory;
this.clipBoardManager = clipBoardManager;
this.taskExecutor = taskExecutor;
this.preferences = prefs;
}

public ExportToClipboardAction(LibraryTab panel, DialogService dialogService, ExporterFactory exporterFactory, ClipBoardManager clipBoardManager, TaskExecutor taskExecutor) {
public ExportToClipboardAction(LibraryTab panel, DialogService dialogService, ExporterFactory exporterFactory, ClipBoardManager clipBoardManager, TaskExecutor taskExecutor, PreferencesService prefs) {
this.panel = panel;
this.dialogService = dialogService;
this.exporterFactory = exporterFactory;
this.clipBoardManager = clipBoardManager;
this.taskExecutor = taskExecutor;
this.preferences = prefs;

}

@Override
Expand All @@ -81,7 +85,7 @@ public void execute() {

// Find default choice, if any
Exporter defaultChoice = exporters.stream()
.filter(exporter -> exporter.getName().equals(Globals.prefs.getImportExportPreferences().getLastExportExtension()))
.filter(exporter -> exporter.getName().equals(preferences.getImportExportPreferences().getLastExportExtension()))
.findAny()
.orElse(null);

Expand All @@ -102,12 +106,12 @@ private ExportResult exportToClipboard(Exporter exporter) throws Exception {
// Set the global variable for this database's file directory before exporting,
// so formatters can resolve linked files correctly.
// (This is an ugly hack!)
Globals.prefs.fileDirForDatabase = panel.getBibDatabaseContext()
.getFileDirectories(Globals.prefs.getFilePreferences());
preferences.storeFileDirforDatabase(panel.getBibDatabaseContext()
.getFileDirectories(preferences.getFilePreferences()));

// Add chosen export type to last used preference, to become default
Globals.prefs.storeImportExportPreferences(
Globals.prefs.getImportExportPreferences().withLastExportExtension(exporter.getName()));
preferences.storeImportExportPreferences(
preferences.getImportExportPreferences().withLastExportExtension(exporter.getName()));

Path tmp = null;
try {
Expand All @@ -122,7 +126,7 @@ private ExportResult exportToClipboard(Exporter exporter) throws Exception {
panel.getBibDatabaseContext()
.getMetaData()
.getEncoding()
.orElse(Globals.prefs.getDefaultEncoding()),
.orElse(preferences.getDefaultEncoding()),
entries);
// Read the file and put the contents on the clipboard:

Expand Down Expand Up @@ -159,7 +163,7 @@ private String readFileToString(Path tmp) throws IOException {
try (BufferedReader reader = Files.newBufferedReader(tmp, panel.getBibDatabaseContext()
.getMetaData()
.getEncoding()
.orElse(Globals.prefs.getDefaultEncoding()))) {
.orElse(preferences.getDefaultEncoding()))) {
return reader.lines().collect(Collectors.joining(OS.NEWLINE));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/jabref/gui/maintable/RightClickMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ private static Menu createCopySubMenu(LibraryTab libraryTab,
copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.COPY_CITATION_PREVIEW, new CopyCitationAction(CitationStyleOutputFormat.HTML, dialogService, stateManager, clipBoardManager, previewPreferences)));
}

copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.EXPORT_TO_CLIPBOARD, new ExportToClipboardAction(libraryTab, dialogService, Globals.exportFactory, clipBoardManager, Globals.TASK_EXECUTOR)));
copySpecialMenu.getItems().add(factory.createMenuItem(StandardActions.EXPORT_TO_CLIPBOARD, new ExportToClipboardAction(libraryTab, dialogService, Globals.exportFactory, clipBoardManager, Globals.TASK_EXECUTOR, preferencesService)));
return copySpecialMenu;
}
}
8 changes: 7 additions & 1 deletion src/main/java/org/jabref/preferences/JabRefPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -1049,12 +1049,18 @@ public void importPreferences(Path file) throws JabRefException {
}
}

private FileLinkPreferences getFileLinkPreferences() {
@Override
public FileLinkPreferences getFileLinkPreferences() {
return new FileLinkPreferences(
get(MAIN_FILE_DIRECTORY), // REALLY HERE?
fileDirForDatabase);
}

@Override
public void storeFileDirforDatabase(List<Path> dirs) {
this.fileDirForDatabase = dirs;
}

Comment on lines +1052 to +1063
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getFileLinkPreferences needs to be removed in the future as well as the variable fileDirForDatabase, since this is some pretty dirty hack from earlier times in JabRef that needs to be done right sometime. So changing something here is lost effort. Furthermore storeFileDirforDatabase does not persistently store a variable, therefor the naming scheme get/store should not be used here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this variable is better located in the stateManager? @tobiasdiez

@Override
public LayoutFormatterPreferences getLayoutFormatterPreferences(JournalAbbreviationRepository repository) {
return new LayoutFormatterPreferences(
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/jabref/preferences/PreferencesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.logic.l10n.Language;
import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.logic.layout.format.FileLinkPreferences;
import org.jabref.logic.layout.format.NameFormatterPreferences;
import org.jabref.logic.net.ProxyPreferences;
import org.jabref.logic.openoffice.OpenOfficePreferences;
Expand Down Expand Up @@ -278,6 +279,10 @@ public interface PreferencesService {

void storeShouldAutosave(boolean shouldAutosave);

FileLinkPreferences getFileLinkPreferences();

void storeFileDirforDatabase(List<Path> dirs);

Comment on lines +282 to +285
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. This setter is no fix for the hack, but a disguise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep it ugly, until it's fixed. 😏

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I would, but it needs to be present in the preferences Service, because we need to get rid of the Globals for the test, as we cannot mock it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would vote to create a follow up PR to fix this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a follow-up is most urgent needed, but there was a reason for that hack, I'm afraid, there is no easy fix for this. See declaration of this:

    // The following field is used as a global variable during the export of a database.
    // By setting this field to the path of the database's default file directory, formatters
    // that should resolve external file paths can access this field. This is an ugly hack
    // to solve the problem of formatters not having access to any context except for the
    // string to be formatted and possible formatter arguments.
    public List<Path> fileDirForDatabase;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But to say that again with most deep concern: The proposed change, to put these two methods into the preferencesService shall not happen!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After careful consideration: @Siedlerchr and me think it would be right, to keep the changes as SiedlerChr added, since extracting this mess of a temp variable in the preferences totally exploding this PR. This calls for a distinct one. Good luck, @Siedlerchr 😆

//*************************************************************************************************************
// Import/Export preferences
//*************************************************************************************************************
Expand Down
168 changes: 168 additions & 0 deletions src/test/java/org/jabref/gui/edit/CopyMoreActionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.jabref.gui.edit;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import org.jabref.gui.ClipBoardManager;
import org.jabref.gui.DialogService;
import org.jabref.gui.JabRefDialogService;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.StandardActions;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.types.StandardEntryType;
import org.jabref.preferences.PreferencesService;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class CopyMoreActionTest {

private CopyMoreAction copyMoreAction;
private DialogService dialogService = spy(DialogService.class);
private ClipBoardManager clipBoardManager = mock(ClipBoardManager.class);
private PreferencesService preferencesService = mock(PreferencesService.class);
private StateManager stateManager = mock(StateManager.class);
private BibEntry entry;
private List<String> titles = new ArrayList<String>();
private List<String> keys = new ArrayList<String>();

@BeforeEach
public void setUp() {
String title = "A tale from the trenches";
entry = new BibEntry(StandardEntryType.Misc)
.withField(StandardField.AUTHOR, "Souti Chattopadhyay and Nicholas Nelson and Audrey Au and Natalia Morales and Christopher Sanchez and Rahul Pandita and Anita Sarma")
.withField(StandardField.TITLE, title)
.withField(StandardField.YEAR, "2020")
.withField(StandardField.DOI, "10.1145/3377811.3380330")
.withField(StandardField.SUBTITLE, "cognitive biases and software development")
.withCitationKey("abc");
titles.add(title);
keys.add("abc");
}

@Test
public void testExecuteOnFail() {
when(stateManager.getActiveDatabase()).thenReturn(Optional.empty());
when(stateManager.getSelectedEntries()).thenReturn(FXCollections.emptyObservableList());
copyMoreAction = new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

verify(clipBoardManager, times(0)).setContent(any(String.class));
verify(dialogService, times(0)).notify(any(String.class));
}

@Test
public void testExecuteCopyTitleWithNoTitle() {
BibEntry entryWithNoTitle = (BibEntry) entry.clone();
entryWithNoTitle.clearField(StandardField.TITLE);
ObservableList<BibEntry> entriesWithNoTitles = FXCollections.observableArrayList(entryWithNoTitle);
BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoTitles));

when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoTitles);
copyMoreAction = new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

verify(clipBoardManager, times(0)).setContent(any(String.class));
verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have titles."));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I remember that junit provides something like any() as an argument replacement, since dealing with translations in tests that are not especially tailored for language test can mess things quickly up.

}

@Test
public void testExecuteCopyTitleOnPartialSuccess() {
BibEntry entryWithNoTitle = (BibEntry) entry.clone();
entryWithNoTitle.clearField(StandardField.TITLE);
ObservableList<BibEntry> mixedEntries = FXCollections.observableArrayList(entryWithNoTitle, entry);
BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries));

when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
when(stateManager.getSelectedEntries()).thenReturn(mixedEntries);
copyMoreAction = new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

String copiedTitles = String.join("\n", titles);
verify(clipBoardManager, times(1)).setContent(copiedTitles);
verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined title.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: can the localized argument be exchanged with something generic?
There are more below...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, any()

Copy link
Contributor Author

@ningxie1991 ningxie1991 Apr 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they can by just any() but because each test is testing for a specific condition, for example when no entries have titles, or when some entries have titles. In different cases, the diaglogService notitfy shows different corresponding messages. I thought it would be better to specify the messages that we are verifying, otherwise the verify dialogService kind of loses its purpose if we use any() as the argument? I am not sure if verifying that dialogService gets invoked is enough in this test. If so, I can definitely switch them to any() arguments. @calixtus

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay to test the correct invocation. I think @calixtus and I thought verify only check with times the number of invovations.

Integer.toString(mixedEntries.size() - titles.size()), Integer.toString(mixedEntries.size())));
}

@Test
public void testExecuteCopyTitleOnSuccess() {
ObservableList<BibEntry> entriesWithTitles = FXCollections.observableArrayList(entry);
BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithTitles));

when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
when(stateManager.getSelectedEntries()).thenReturn(entriesWithTitles);
copyMoreAction = new CopyMoreAction(StandardActions.COPY_TITLE, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

String copiedTitles = String.join("\n", titles);
verify(clipBoardManager, times(1)).setContent(copiedTitles);
verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.",
JabRefDialogService.shortenDialogMessage(copiedTitles)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test is becoming pretty complicated: in fact you are also testing the shortenDialogMessage-method here too...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, I just realized this shortenDialogMessage method is being tested as well. However, as I mentioned in a comment above, the reason why I used specific localized argument was because the tests are meant to verify if dialogSerivce is showing the correct message under each condition. Maybe here I can just simply use any() as the mocked argument since it's too complicated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's okay.

}

@Test
public void testExecuteCopyKeyWithNoKey() {
BibEntry entryWithNoKey = (BibEntry) entry.clone();
entryWithNoKey.clearCiteKey();
ObservableList<BibEntry> entriesWithNoKeys = FXCollections.observableArrayList(entryWithNoKey);
BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithNoKeys));

when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
when(stateManager.getSelectedEntries()).thenReturn(entriesWithNoKeys);
copyMoreAction = new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

verify(clipBoardManager, times(0)).setContent(any(String.class));
verify(dialogService, times(1)).notify(Localization.lang("None of the selected entries have citation keys."));
}

@Test
public void testExecuteCopyKeyOnPartialSuccess() {
BibEntry entryWithNoKey = (BibEntry) entry.clone();
entryWithNoKey.clearCiteKey();
ObservableList<BibEntry> mixedEntries = FXCollections.observableArrayList(entryWithNoKey, entry);
BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(mixedEntries));

when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
when(stateManager.getSelectedEntries()).thenReturn(mixedEntries);
copyMoreAction = new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

String copiedKeys = String.join("\n", keys);
verify(clipBoardManager, times(1)).setContent(copiedKeys);
verify(dialogService, times(1)).notify(Localization.lang("Warning: %0 out of %1 entries have undefined citation key.",
Integer.toString(mixedEntries.size() - titles.size()), Integer.toString(mixedEntries.size())));
}

@Test
public void testExecuteCopyKeyOnSuccess() {
ObservableList<BibEntry> entriesWithKeys = FXCollections.observableArrayList(entry);
BibDatabaseContext databaseContext = new BibDatabaseContext(new BibDatabase(entriesWithKeys));

when(stateManager.getActiveDatabase()).thenReturn(Optional.ofNullable(databaseContext));
when(stateManager.getSelectedEntries()).thenReturn(entriesWithKeys);
copyMoreAction = new CopyMoreAction(StandardActions.COPY_KEY, dialogService, stateManager, clipBoardManager, preferencesService);
copyMoreAction.execute();

String copiedKeys = String.join("\n", keys);
verify(clipBoardManager, times(1)).setContent(copiedKeys);
verify(dialogService, times(1)).notify(Localization.lang("Copied '%0' to clipboard.",
JabRefDialogService.shortenDialogMessage(copiedKeys)));
}
}
Loading