2017-09-02 3 views
0

J'essaie d'écrire des tests Selenium qui vérifient les problèmes de mise en page. Pour cela j'utilise Selenium Webdriver du côté Java et phantomjs le "navigateur". Je veux utiliser phantomjs car il est capable de faire des captures d'écran des composants réellement rendus.Comment désactiver l'antialiasing de police sur phantomjs avec webdriver (Java)?

Par défaut, phantomjs rend le texte à l'aide de l'anti-aliasing, ce qui rend la numérisation des textes difficile (pour trouver des lignes de base de texte et effectuer une OCR simple).

Comment puis-je dire à phantomJS de ne pas utiliser l'anti-crénelage?

Répondre

0

J'ai utilisé l'astuce suivante pour désactiver phantomjs anti aliasing sous Linux. PhantomJS est construit en utilisant fontconfig, et cette bibliothèque recherche un fichier "fonts.conf" dans plusieurs endroits: voir https://www.freedesktop.org/software/fontconfig/fontconfig-user.html.

En créant le fonts.conf suivant, nous pouvons désactiver l'antialiasing pour fontconfig:

<match target="font"> 
    <edit mode="assign" name="antialias"> 
    <bool>false</bool> 
    </edit> 
</match> 

L'un des emplacements est définie par une variable d'environnement, selon les spécifications: $ XDG_CONFIG_HOME/fontconfig/fonts.conf . Donc, en créant un fichier temporaire comme /tmp/phantomjs-config/fontconfig/fonts.conf avec le contenu ci-dessus, puis en définissant XDG_CONFIG_HOME dans/tmp/phantomjs-config, nous disons à fontconfig de lire ce fichier.

Il y a cependant un problème: la classe PhantomJSDriver, par défaut, ne permet pas de définir les variables d'environnement. Ce qui est triste, car le code de travail sous-jacent, PhantomWebDriverService, le permet.

Pour résoudre cela, je créé une petite classe d'aide dans laquelle je copiais des méthodes protégées de PhantomJSDriverService:

public class MyPhantomDriverService { 
    public static PhantomJSDriverService createDefaultService(Capabilities desiredCapabilities, Map<String, String> env) { 
     Proxy proxy = null; 
     if (desiredCapabilities != null) { 
      proxy = Proxy.extractFrom(desiredCapabilities); 
     } 

     File phantomjsfile = findPhantomJS(desiredCapabilities, "https://github.com/ariya/phantomjs/wiki", "http://phantomjs.org/download.html"); 
     File ghostDriverfile = findGhostDriver(desiredCapabilities, "https://github.com/detro/ghostdriver/blob/master/README.md", "https://github.com/detro/ghostdriver/downloads"); 
     Builder builder = new Builder(); 
     builder.usingPhantomJSExecutable(phantomjsfile) 
      .usingGhostDriver(ghostDriverfile) 
      .usingAnyFreePort() 
      .withProxy(proxy) 
      .withLogFile(new File("phantomjsdriver.log")) 
      .usingCommandLineArguments(findCLIArgumentsFromCaps(desiredCapabilities, "phantomjs.cli.args")) 
      .usingGhostDriverCommandLineArguments(findCLIArgumentsFromCaps(desiredCapabilities, "phantomjs.ghostdriver.cli.args")); 
     if(null != env) 
      builder.withEnvironment(env); 
     return builder.build(); 
    } 

    public static File findPhantomJS(Capabilities desiredCapabilities, String docsLink, String downloadLink) { 
     String phantomjspath; 
     if (desiredCapabilities != null && desiredCapabilities.getCapability("phantomjs.binary.path") != null) { 
      phantomjspath = (String)desiredCapabilities.getCapability("phantomjs.binary.path"); 
     } else { 
      phantomjspath = (new ExecutableFinder()).find("phantomjs"); 
      phantomjspath = System.getProperty("phantomjs.binary.path", phantomjspath); 
     } 

     Preconditions.checkState(phantomjspath != null, "The path to the driver executable must be set by the %s capability/system property/PATH variable; for more information, see %s. The latest version can be downloaded from %s", "phantomjs.binary.path", docsLink, downloadLink); 
     File phantomjs = new File(phantomjspath); 
     checkExecutable(phantomjs); 
     return phantomjs; 
    } 

    protected static File findGhostDriver(Capabilities desiredCapabilities, String docsLink, String downloadLink) { 
     String ghostdriverpath; 
     if (desiredCapabilities != null && desiredCapabilities.getCapability("phantomjs.ghostdriver.path") != null) { 
      ghostdriverpath = (String)desiredCapabilities.getCapability("phantomjs.ghostdriver.path"); 
     } else { 
      ghostdriverpath = System.getProperty("phantomjs.ghostdriver.path"); 
     } 

     if (ghostdriverpath != null) { 
      File ghostdriver = new File(ghostdriverpath); 
      Preconditions.checkState(ghostdriver.exists(), "The GhostDriver does not exist: %s", ghostdriver.getAbsolutePath()); 
      Preconditions.checkState(ghostdriver.isFile(), "The GhostDriver is a directory: %s", ghostdriver.getAbsolutePath()); 
      Preconditions.checkState(ghostdriver.canRead(), "The GhostDriver is not a readable file: %s", ghostdriver.getAbsolutePath()); 
      return ghostdriver; 
     } else { 
      return null; 
     } 
    } 

    protected static void checkExecutable(File exe) { 
     Preconditions.checkState(exe.exists(), "The driver executable does not exist: %s", exe.getAbsolutePath()); 
     Preconditions.checkState(!exe.isDirectory(), "The driver executable is a directory: %s", exe.getAbsolutePath()); 
     Preconditions.checkState(exe.canExecute(), "The driver is not executable: %s", exe.getAbsolutePath()); 
    } 

    private static String[] findCLIArgumentsFromCaps(Capabilities desiredCapabilities, String capabilityName) { 
     if (desiredCapabilities != null) { 
      Object cap = desiredCapabilities.getCapability(capabilityName); 
      if (cap != null) { 
       if (cap instanceof String[]) { 
        return (String[])((String[])cap); 
       } 

       if (cap instanceof Collection) { 
        try { 
         Collection<String> capCollection = (Collection<String>)cap; 
         return (String[])capCollection.toArray(new String[capCollection.size()]); 
        } catch (Exception var4) { 
         System.err.println(String.format("Unable to set Capability '%s' as it was neither a String[] or a Collection<String>", capabilityName)); 
        } 
       } 
      } 
     } 

     return new String[0]; 
    } 
} 

Avec ce nouveau code, je peux maintenant créer un PhantomJSDriver comme suit:

 //-- 1. Make a temp directory which will contain our fonts.conf 
     String tmp = System.getProperty("java.io.tmpdir"); 
     if(tmp == null) { 
      tmp = "/tmp"; 
     } 
     File dir = new File(tmp + File.separator + "/_phantomjs-config/fontconfig"); 
     dir.mkdirs(); 
     if(! dir.exists()) { 
      throw new IOException("Can't create fontconfig directory to override phantomjs font settings at " + dir); 
     } 

     File conf = new File(dir, "fonts.conf"); 
     String text = "<match target=\"font\">\n" 
      + "<edit mode=\"assign\" name=\"antialias\">\n" 
      + "<bool>false</bool>\n" 
      + "</edit>\n" 
      + "</match>"; 
     try(FileOutputStream fos = new FileOutputStream(conf)) { 
      fos.write(text.getBytes("UTF-8")); 
     } 

     //-- Set the XDG_CONFIG_HOME envvar; this is used by fontconfig as one of its locations 

     Map<String, String> env = new HashMap<>(); 
     env.put("XDG_CONFIG_HOME", dir.getParentFile().getAbsolutePath()); 

     PhantomJSDriverService service = MyPhantomDriverService.createDefaultService(capabilities, env); 
     wd = new PhantomJSDriver(service, capabilities); 

Problèmes avec le code

Le problème le plus important avec ce code est qu'il peut échouer s'il existe des fichiers fonts.conf (comme $ HOME/.fonts.conf). à définir anti aliasing. Mais pour mon test, cela fonctionne bien.

La même astuce fonctionne aussi pour Headless Chrome;)

Pour désactiver l'anti aliasing sur le chrome sans tête utiliser le même code ci-dessus pour générer le fichier fonts.conf, puis allouer une WebDriver Chrome comme suit:

import org.openqa.selenium.chrome.ChromeDriver; 
import org.openqa.selenium.chrome.ChromeDriverService; 
import org.openqa.selenium.chrome.ChromeDriverService.Builder; 

... 

Map<String, String> env = new HashMap<>(); 
env.put("XDG_CONFIG_HOME", dir.getParentFile().getAbsolutePath()); 

Builder builder = new Builder(); 
builder.usingAnyFreePort(); 
builder.withEnvironment(env); 
ChromeDriverService service = builder.build(); 
return new ChromeDriver(service, dc); 
+0

Je vous suggère de commencer à vous en éloigner. Comme il n'est plus supporté maintenant. https://groups.google.com/forum/#!topic/phantomjs/9aI5d-LDuNE –

+0

Je sais, mais les gens l'utilisent encore. Transition à quelque chose d'autre n'est pas fait aussi rapidement;) – fjalvingh

+0

En fait, la même astuce fonctionne également avec Chrome sans tête;) Je vais mettre à jour la réponse avec ça aussi. – fjalvingh