123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- package com.vaadin.screenshotbrowser;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Properties;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- import org.apache.commons.io.FileUtils;
- import org.apache.commons.io.filefilter.DirectoryFileFilter;
- import org.apache.commons.io.filefilter.SuffixFileFilter;
-
- import com.vaadin.event.ShortcutAction.KeyCode;
- import com.vaadin.event.ShortcutListener;
- import com.vaadin.server.ExternalResource;
- import com.vaadin.server.FileResource;
- import com.vaadin.server.VaadinRequest;
- import com.vaadin.ui.BrowserFrame;
- import com.vaadin.ui.Button;
- import com.vaadin.ui.Button.ClickListener;
- import com.vaadin.ui.CustomComponent;
- import com.vaadin.ui.HorizontalLayout;
- import com.vaadin.ui.Label;
- import com.vaadin.ui.UI;
- import com.vaadin.ui.VerticalLayout;
- import com.vaadin.v7.data.util.BeanItemContainer;
- import com.vaadin.v7.ui.Table;
-
- public class ScreenshotBrowser extends UI {
- private static final File screenshotDir = findScreenshotDir();
-
- /*-
- * Groups:
- * 1 - test class
- * 2 - test method
- * 3 - platform
- * 4 - browser name
- * 5 - browser version
- * 6 - identifier
- * 7 - additional identifiers
- */
- private static final Pattern screenshotNamePattern = Pattern
- .compile("(.+?)-(.+?)_(.+?)_(.+?)_(.*?)_(.+?)(_.+)?\\.png\\.html");
-
- public static enum Action {
- ACCEPT {
- @Override
- public void apply(File screenshotFile) {
- File targetFile = new File(getReferenceDir(),
- screenshotFile.getName());
-
- // Delete previous file as well as any alternatives
- if (targetFile.exists()) {
- for (int i = 1; true; i++) {
- File alternative = getAlternative(targetFile, i);
- if (alternative.exists()) {
- alternative.delete();
- } else {
- break;
- }
- }
- targetFile.delete();
- }
-
- screenshotFile.renameTo(targetFile);
- }
- },
- IGNORE {
- @Override
- public void apply(File screenshotFile) {
- screenshotFile.delete();
- }
- },
- ALTERNATIVE {
- @Override
- public void apply(File screenshotFile) {
- File baseFile = new File(getReferenceDir(),
- screenshotFile.getName());
-
- // Iterate until we find the first alternative id not yet used
- File targetFile = baseFile;
- int alternativeNumber = 1;
- while (targetFile.exists()) {
- targetFile = getAlternative(baseFile, alternativeNumber++);
- }
-
- screenshotFile.renameTo(targetFile);
- }
- };
-
- public void commit(File htmlFile) {
- String screenshotName = htmlFile.getName().substring(0,
- htmlFile.getName().length() - ".html".length());
-
- apply(new File(htmlFile.getParentFile(), screenshotName));
-
- htmlFile.delete();
- }
-
- private static File getReferenceDir() {
- return new File(screenshotDir, "reference-screenshots");
- }
-
- private static File getAlternative(File baseFile,
- int alternativeNumber) {
- assert alternativeNumber >= 1;
- String alternativeName = baseFile.getName().replaceFirst("\\.png",
- "_" + alternativeNumber + ".png");
- return new File(baseFile.getParentFile(), alternativeName);
- }
-
- protected abstract void apply(File screenshotFile);
- }
-
- public static class ComparisonFailure {
- private final Matcher matcher;
- private final File file;
-
- private Action action;
-
- public ComparisonFailure(File file) {
- this.file = file;
- matcher = screenshotNamePattern.matcher(file.getName());
- if (!matcher.matches()) {
- throw new RuntimeException("Could not parse screenshot name "
- + file.getAbsolutePath());
- }
- }
-
- public File getFile() {
- return file;
- }
-
- public String getName() {
- return matcher.group();
- }
-
- public String getTestClass() {
- return matcher.group(1);
- }
-
- public String getTestMethod() {
- return matcher.group(2);
- }
-
- public String getBrowser() {
- return matcher.group(4) + " " + matcher.group(5);
- }
-
- public String getIdentifier() {
- String additional = matcher.group(7);
- if (additional != null) {
- return matcher.group(6) + additional;
- }
- return matcher.group(6);
- }
-
- public void setAction(Action action) {
- this.action = action;
- }
-
- public Action getAction() {
- return action;
- }
- }
-
- private class Viewer extends CustomComponent {
- private BrowserFrame preview = new BrowserFrame();
- private VerticalLayout left = new VerticalLayout();
- private HorizontalLayout root = new HorizontalLayout(left, preview);
-
- private Collection<ComparisonFailure> items;
-
- public Viewer() {
- preview.setWidth("1500px");
- preview.setHeight("100%");
-
- left.setMargin(true);
- left.setSpacing(true);
- left.setSizeUndefined();
- left.setWidth("270px");
-
- left.addComponent(
- createActionButton("Accept changes", 'j', Action.ACCEPT));
- left.addComponent(
- createActionButton("Ignore changes", 'k', Action.IGNORE));
- left.addComponent(createActionButton("Use as alternative", 'l',
- Action.ALTERNATIVE));
- left.addComponent(
- new Button("Clear action", createSetActionListener(null)));
-
- left.addComponent(createSpacer());
- left.addComponent(
- new Button("Commit actions", event -> commitActions()));
-
- left.addComponent(createSpacer());
- left.addComponent(
- new Button("Refresh", event -> refreshTableContainer()));
-
- Label expandSpacer = createSpacer();
- left.addComponent(expandSpacer);
- left.setExpandRatio(expandSpacer, 1);
-
- Label instructions = new Label(
- "Press the j, k or l keys to quickly select an action for the selected item.");
- instructions.setWidth("100%");
- left.addComponent(instructions);
-
- root.setExpandRatio(left, 1);
- root.setSizeFull();
-
- setCompositionRoot(root);
- setHeight("850px");
- setWidth("1800px");
- }
-
- private Button createActionButton(String caption, char shortcut,
- Action action) {
- Button button = new Button(
- caption + " <strong>" + shortcut + "</strong>",
- createSetActionListener(action));
- button.setCaptionAsHtml(true);
- if (!Action.IGNORE.equals(action)) {
- // other actions disabled for now since the functionality was
- // designed for a different directory structure, needs reworking
- button.setEnabled(false);
- }
- return button;
- }
-
- private Label createSpacer() {
- // Poor man's spacer, non-breaking space
- return new Label("\u00a0");
- }
-
- private ClickListener createSetActionListener(final Action action) {
- return event -> setActions(action);
- }
-
- public void setActions(final Action action) {
- for (ComparisonFailure comparisonFailure : items) {
- comparisonFailure.setAction(action);
- }
- table.refreshRowCache();
- }
-
- public void setItems(Collection<ComparisonFailure> items) {
- this.items = items;
-
- if (items.size() == 1) {
- ComparisonFailure failure = items.iterator().next();
- preview.setSource(new FileResource(failure.getFile()));
- } else {
- preview.setSource(new ExternalResource("about:blank"));
- }
- }
- }
-
- private final Table table = new Table();
- private final Viewer viewer = new Viewer();
-
- @Override
- protected void init(VaadinRequest request) {
- table.setWidth("100%");
- table.setPageLength(10);
-
- table.setMultiSelect(true);
- table.addValueChangeListener(event -> {
-
- @SuppressWarnings("unchecked")
- Collection<ComparisonFailure> selectedRows = (Collection<ComparisonFailure>) table
- .getValue();
-
- viewer.setItems(selectedRows);
- });
-
- table.addShortcutListener(
- createShortcutListener(KeyCode.J, Action.ACCEPT));
- table.addShortcutListener(
- createShortcutListener(KeyCode.K, Action.IGNORE));
- table.addShortcutListener(
- createShortcutListener(KeyCode.L, Action.ALTERNATIVE));
-
- refreshTableContainer();
-
- VerticalLayout mainLayout = new VerticalLayout(viewer, table);
- mainLayout.setSizeUndefined();
-
- setSizeFull();
- setContent(mainLayout);
-
- table.focus();
- }
-
- private void commitActions() {
- for (ComparisonFailure comparisonFailure : getContainer()
- .getItemIds()) {
- Action action = comparisonFailure.getAction();
- if (action != null) {
- action.commit(comparisonFailure.getFile());
- }
- }
-
- refreshTableContainer();
- }
-
- private ShortcutListener createShortcutListener(int keyCode,
- final Action action) {
- return new ShortcutListener(action.toString(), keyCode, null) {
- @Override
- public void handleAction(Object sender, Object target) {
- viewer.setActions(action);
- selectNextWithoutAction();
- }
- };
- }
-
- private void selectNextWithoutAction() {
- Collection<?> selected = (Collection<?>) table.getValue();
- BeanItemContainer<ComparisonFailure> container = getContainer();
-
- // Find where to start
- ComparisonFailure candidate;
- if (selected == null || selected.isEmpty()) {
- candidate = container.firstItemId();
- } else {
- candidate = (ComparisonFailure) selected.iterator().next();
- }
-
- // Find first one without action
- while (candidate != null && candidate.getAction() != null) {
- candidate = container.nextItemId(candidate);
- }
-
- // Select it
- if (candidate == null) {
- table.setValue(Collections.emptySet());
- } else {
- table.setValue(Collections.singleton(candidate));
- }
- }
-
- @SuppressWarnings("unchecked")
- private BeanItemContainer<ComparisonFailure> getContainer() {
- return (BeanItemContainer<ComparisonFailure>) table
- .getContainerDataSource();
- }
-
- private void refreshTableContainer() {
- File errorsDir = new File(screenshotDir, "error-screenshots");
-
- Collection<File> failures = FileUtils.listFiles(errorsDir,
- new SuffixFileFilter(".html"), DirectoryFileFilter.DIRECTORY);
-
- BeanItemContainer<ComparisonFailure> container = new BeanItemContainer<>(
- ComparisonFailure.class);
- for (File failure : failures) {
- container.addBean(new ComparisonFailure(failure));
- }
-
- table.setContainerDataSource(container);
- table.setVisibleColumns("testClass", "testMethod", "browser",
- "identifier", "action");
- if (container.size() > 0) {
- table.select(container.firstItemId());
- }
- }
-
- private static File findScreenshotDir() {
- File propertiesFile = new File(
- "../work/eclipse-run-selected-test.properties");
- if (!propertiesFile.exists()) {
- throw new RuntimeException(
- "File " + propertiesFile.getAbsolutePath() + " not found.");
- }
-
- FileInputStream in = null;
- try {
- in = new FileInputStream(propertiesFile);
-
- Properties properties = new Properties();
- properties.load(in);
- String screenShotDirName = properties
- .getProperty("com.vaadin.testbench.screenshot.directory");
- if (screenShotDirName == null
- || screenShotDirName.startsWith("<")) {
- throw new RuntimeException(
- "com.vaadin.testbench.screenshot.directory has not been configred in "
- + propertiesFile.getAbsolutePath());
- }
- File screenshotDir = new File(screenShotDirName);
- if (!screenshotDir.isDirectory()) {
- throw new RuntimeException(screenshotDir.getAbsolutePath()
- + " is not a directory");
- }
- return screenshotDir;
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- try {
- if (in != null) {
- in.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
|