GabrielTrigo / TSWebDriver4Delphi

TSWebDriver4Delphi is a library for Web Testing and Automation
MIT License
41 stars 13 forks source link

How run chromedriver in background #2

Open baxing opened 5 months ago

baxing commented 5 months ago

When running the application, chromedriver will also be called. You will see the chromedriver window pop up. Can we have this chromedriver run in the background or minimize it automatically?

Thank you

GabrielTrigo commented 5 months ago

great idea

baxing commented 5 months ago

Please update the commands that can be done. 😃

Thank you

rfwoolf commented 4 months ago

Here is my implementation which hides the Driver window and/or the browser windows. (See the variable " HideDriverWindow := true;", and modify the "Start" function to accept it);

function StartWebSession: Boolean;
var
  WebDriverPath : string;
  port : integer;
  ChromePath : string;
  ChromeProfilesPath : string;
  HomePageURL : string;

HideDriverWindow : boolean;
begin
  result := false;

 //Initialise
//I put the webdriver in the same folder as the chrome.exe. Make sure it is the right version for the chrome you are using
  WebDriverPath := 'C:\xxx\xxx\xxx\xxx\GoogleChromePortable\App\Chrome-bin\webdriver.exe';
//The default port is 9515. If you are running multiple servers / automations, then each one should have its own port
  port := 9516
  ChromePath :='C:\xxx\xxx\xx\xxx\GoogleChromePortable\App\Chrome-bin\chrome.exe';
//The 'Data' directoriess can contain multiple profiles. Do not link to the profile itself, just the containing folder
  ChromeProfilesPath := 'C:\xxx\xxx\xxx\xxx\GoogleChromePortable\Data';
  HideDriverWindow := true;

  HomePageURL := 'www.example.com';
  //ReportMemoryLeaksOnShutdown := True;

  ///////////////////
  //Create the "Driver" (Server)
  ///////////////////
  FDriver := TTSWebDriver.New.Driver();

  //WebDriver
  WebDriverPath := StringReplace(WebDriverPath, '\', '/', [rfReplaceAll]);
  FDriver.Options.DriverPath(WebDriverPath);

  //ChromePath
  ChromePath := StringReplace(ChromePath, '\', '/', [rfReplaceAll]);
  FDriver.Options.BinaryPath(ChromePath);

  //////////////
  //Create the ChromeDriver
  //////////////
  FChromeDriver := FDriver.Browser().Chrome();
  FChromeDriver.AddArgument('disable-blink-features=AutomationControlled');
  FChromeDriver.AddArgument('excludeSwitches','enable-automation');
  FChromeDriver.AddArgument('useAutomationExtension','False');
  FChromeDriver.AddArgument('binary', ChromePath);
//If using Headlessmode: 
  //FChromeDriver.AddArgument('--headless=new');
  //User data dir
  ChromeProfilesPath := StringReplace(ChromeProfilesPath, '\', '/', [rfReplaceAll]);
  FChromeDriver.AddArgument('user-data-dir', ChromeProfilesPath);

  //.AddArgument('window-size', '1000,800')

  //////////////
  //Start the server!
  //////////////
  //"Driver" (Server) Start
  //Here I have modified the 'Start' function to accept these other parameters (see code below)
  FDriver.Start(WebDriverPath, port, ChromePath, HideDriverWindow);

  //New Session
  FChromeDriver.NewSession();

  //Set webdriver to undefined
  FChromeDriver.ExecuteSyncScript('Object.defineProperty(navigator, ''webdriver'', {get: () => undefined})'); //{"script":"return document.title","parameters":{},"args":[]}

  //Network.setUserAgentOverride
  //============================
  //I was unable to execute 'Network.setUserAgentOverride' despite the right syntax due to this issue:
  //The Network.setUserAgentOverride command is part of the Chrome DevTools Protocol and must be executed within a context that supports this protocol.
  //Directly embedding this command in JavaScript running in the browser's console will not work, hence the "Network is not defined" error.
  //FChromeDriver.ExecuteSyncScript('Network.setUserAgentOverride', '{"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"}');

  //Navigate to...
  HomePageURL := StringReplace(HomePageURL, '\', '/', [rfReplaceAll]);
  FChromeDriver.NavigateTo(HomePageURL);

  result := true;
end;

Now modify the wrapper: In unit TSWebDriver.Interfaces; change function Start(): ITSWebDriverBase; to: function Start(executable_path : string; port : integer; binary_location : string; hidden: boolean = false): ITSWebDriverBase;

Now modify the unit unit TSWebDriver.Driver;: change function Start(): ITSWebDriverBase; to function Start(executable_path : string; port : integer ; binary_location : string; hidden: boolean = false): ITSWebDriverBase;

And make the function look like this:

function TTSWebDriverBase.Start(executable_path : string; port : integer; binary_location : string; hidden: boolean = false): ITSWebDriverBase;
var
  CommandLine: string;
begin
  FProccessName := ExtractFileName(FSWebDriverBaseOptions.DriverPath);

  if port = 0 then
    port := 9515;

  FPort := port;
  if not FileExists(FSWebDriverBaseOptions.DriverPath) then
    raise Exception.Create('driver file not exists.' + FSWebDriverBaseOptions.DriverPath);

  if Self.IsRunning() or (FProcessInfo.hProcess <> 0) then Exit;

  // StartupInfo
  FillChar(FStartupInfo, SizeOf(FStartupInfo), 0);
  if hidden then
    FStartupInfo.wShowWindow := SW_HIDE
  else
    FStartupInfo.wShowWindow := SW_NORMAL;
  FStartupInfo.dwFlags := STARTF_USESHOWWINDOW;

  // ProcessInfo
  FillChar(FProcessInfo, SizeOf(FProcessInfo), 0);

  // Construct the command line with the parameters
  CommandLine := Format('%s --port=%d --executable_path="' + executable_path + '" --binary_location="' + binary_location + '"', [FSWebDriverBaseOptions.DriverPath, port]);
  //CommandLine := Format('%s           --executable_path="' + executable_path + '" --binary_location="' + binary_location + '"', [FSWebDriverBaseOptions.DriverPath]);
  if CreateProcess(nil,                                             // lpApplicationName: PChar;          // pointer to name of executable module
                   PChar(CommandLine),                              // lpCommandLine: PChar;             // pointer to command line string
                   nil,                                             // lpProcessAttributes: PSecurityAttributes; // pointer to process security attributes
                   nil,                                             // lpThreadAttributes: PSecurityAttributes;  // pointer to thread security attributes
                   False,                                           // bInheritHandles: BOOL;            // handle inheritance flag
                   NORMAL_PRIORITY_CLASS,                           // dwCreationFlags: DWORD;           // creation flags
                   nil,                                             // lpEnvironment: Pointer;           // pointer to new environment block
                   nil,                                             // lpCurrentDirectory: PChar;        // pointer to current directory name
                   FStartupInfo,                                    // const lpStartupInfo: TStartupInfo;            // pointer to STARTUPINFO
                   FProcessInfo                                     // var lpProcessInformation: TProcessInformation // pointer to PROCESS_INFORMATION
  ) then
  begin
    // Process started successfully
  end
  else
  begin
    // Handle error
    RaiseLastOSError;
  end;
end;

Modify unit TSWebDriver.Consts;: under interface, add this global variable:

var
  FPort : integer;