<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: tandt53</title>
    <description>The latest articles on DEV Community by tandt53 (@tandt53).</description>
    <link>https://dev.to/tandt53</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F98197%2F24b77e1b-ba7a-467c-9285-49e73db4846b.jpg</url>
      <title>DEV Community: tandt53</title>
      <link>https://dev.to/tandt53</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tandt53"/>
    <language>en</language>
    <item>
      <title>Check your message before Claude Code sees it</title>
      <dc:creator>tandt53</dc:creator>
      <pubDate>Sun, 15 Nov 2026 11:13:10 +0000</pubDate>
      <link>https://dev.to/tandt53/check-your-message-before-claude-code-sees-it-24ho</link>
      <guid>https://dev.to/tandt53/check-your-message-before-claude-code-sees-it-24ho</guid>
      <description>&lt;p&gt;Copy a failing request from DevTools as curl, paste it into an AI coding agent and ask why it returns 401. The &lt;code&gt;Authorization&lt;/code&gt; header goes to the model with the rest of the message. Nothing in Claude Code checks for it first.&lt;/p&gt;

&lt;p&gt;So I added a check that does. It reads each message before it is sent and stops the ones that hold an API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;Developers and testers paste things into AI coding agents all day: a failing curl command, a request copied from DevTools, twenty lines of a CI log, a config file that will not load. Any of those can carry an &lt;code&gt;Authorization&lt;/code&gt; header, a session cookie or a key. Once it is in the message, it goes to the model with everything else, and a key that has left the machine has to be rotated.&lt;/p&gt;

&lt;p&gt;I keep my Claude Code setup in an open-source repository, and this check is one of its hooks: a script Claude Code runs at a fixed point, here on every message before the model sees it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did not work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A rule in the agent's instructions.&lt;/strong&gt; "Never handle secrets" is a rule for the agent. The key comes in with the paste, and by the time the agent reads any rule, the message has already been sent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Masking the key before it is sent.&lt;/strong&gt; My first idea was to find the key, replace it with &lt;code&gt;ghp_…&lt;/code&gt; and send the rest. Claude Code runs a &lt;code&gt;UserPromptSubmit&lt;/code&gt; hook on every message before the model sees it, but that hook can only block a message or let it through. It cannot rewrite it.&lt;/p&gt;

&lt;p&gt;So the check had to be a hook, and a hook that blocks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What worked: block only what is certainly a secret
&lt;/h2&gt;

&lt;p&gt;A guard that blocks on a guess gets bypassed by habit. If every third message is stopped, people start typing the word that sends a message anyway, without reading the warning. So the hook has two levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Block:&lt;/strong&gt; forms that are certainly secrets. GitHub tokens (&lt;code&gt;ghp_&lt;/code&gt;, &lt;code&gt;github_pat_&lt;/code&gt; and the other kinds), Anthropic keys, AWS access key ids, private key blocks, JWTs, and a credential header or session cookie whose value looks real (more on that below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warn only:&lt;/strong&gt; anything else that looks like a secret. When gitleaks, an open-source secret scanner, is installed, the message also goes through &lt;code&gt;gitleaks stdin&lt;/code&gt; with a 2-second budget; on my machine it answers in about 20 ms. A gitleaks rule for one of the certain forms blocks. A generic rule such as &lt;code&gt;generic-api-key&lt;/code&gt; only warns.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it blocks, it says where and what, never the value. Here it stops a curl command in Claude Code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fobq9nyjwatppk8xw91ib.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fobq9nyjwatppk8xw91ib.webp" alt="Claude Code shows &amp;quot;UserPromptSubmit operation blocked by hook&amp;quot;: secret_guard stopped the message, names line 1: GitHub token ghp_ct…5VYy, and says to remove it or start the message with secret-ok" width="799" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The token in the prompt is a fake one, covered here anyway. The hook's own message shows only &lt;code&gt;ghp_ct…5VYy&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The value is masked so the warning does not become one more place the key is written.&lt;/p&gt;

&lt;h2&gt;
  
  
  Headers: check the name or the value?
&lt;/h2&gt;

&lt;p&gt;This was the real design question, because a pasted request is mostly headers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check only the header &lt;strong&gt;name&lt;/strong&gt;, and every doc that says &lt;code&gt;Authorization: Bearer $TOKEN&lt;/code&gt; is blocked.&lt;/li&gt;
&lt;li&gt;Check only the &lt;strong&gt;value&lt;/strong&gt;, and every commit hash and UUID looks like a key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the hook needs both: a credential header (Authorization, X-API-Key, X-Auth-Token and a few more) or a session cookie, &lt;em&gt;and&lt;/em&gt; a value that looks real. "Looks real" is deliberately plain (comments added here):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;looks_real&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"""A header value that is a credential, not a placeholder or a word."""&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# short, a placeholder ($TOKEN, &amp;lt;your-key&amp;gt;, xxxx...), or too few distinct characters&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="no"&gt;PLACEHOLDER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
    &lt;span class="c1"&gt;# a real credential mixes letters and digits&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;"[A-Za-z]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;"\d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$TOKEN&lt;/code&gt;, &lt;code&gt;&amp;lt;your-key&amp;gt;&lt;/code&gt;, &lt;code&gt;xxxxxxxxxxxxxxxx&lt;/code&gt; and &lt;code&gt;process.env.API_KEY&lt;/code&gt; pass as placeholders. A &lt;code&gt;Basic&lt;/code&gt; value is decoded first and blocks only when it holds &lt;code&gt;user:password&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two ways out exist on purpose. &lt;code&gt;[secret-ok]&lt;/code&gt; at the start of a message sends it anyway, with a warning, for the day the "secret" is a test fixture the agent needs to see. &lt;code&gt;CLAUDE_SKIP_SECRET_GUARD=1&lt;/code&gt; turns the hook off. And if the hook itself crashes, the message goes through with a "could not scan" warning: a broken guard must never trap a message.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result, and what it still cannot do
&lt;/h2&gt;

&lt;p&gt;Now a message like the one at the top stops before it is sent. In a test with a fake GitHub token, the block took zero model turns, so nothing was spent.&lt;/p&gt;

&lt;p&gt;The same test showed what a block does not do. The blocked message is not gone: its full text was still written once to the session transcript on disk. The guard keeps a secret from the model, not off the machine. A key that was pasted still needs rotating.&lt;/p&gt;

&lt;p&gt;How I know the hook works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;18 tests&lt;/strong&gt;, with fake keys built at run time, so the repository holds nothing a scanner would take for a key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;18 deliberate bugs.&lt;/strong&gt; I broke a copy of the hook 18 ways and checked that a test failed each time. One break survived at first: removing the distinct-character rule failed no test, because the only dummy value in the tests, &lt;code&gt;xxxx…&lt;/code&gt;, was already caught as a placeholder. A case with &lt;code&gt;ab12ab12ab12ab12ab12&lt;/code&gt; now covers it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd tell any team using AI coding agents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Guard the message, not the agent: the key comes in with the paste.&lt;/li&gt;
&lt;li&gt;Block only certainties and warn on guesses, or the way out becomes muscle memory.&lt;/li&gt;
&lt;li&gt;For headers, judge the name and the value together.&lt;/li&gt;
&lt;li&gt;Know what a block does not do. The text can still sit on disk, so rotate a pasted key anyway.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The hook is one Python file, standard library only: &lt;a href="https://github.com/tandt53/claude-code-setup/blob/main/hooks/secret_guard.py" rel="noopener noreferrer"&gt;hooks/secret_guard.py&lt;/a&gt; in my open-source Claude Code setup (Apache 2.0). &lt;a href="https://github.com/tandt53/claude-code-setup/blob/main/docs/MECHANISMS.md" rel="noopener noreferrer"&gt;docs/MECHANISMS.md&lt;/a&gt; lists what it enforces, what it does not, and how it was tested.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>coding</category>
      <category>security</category>
    </item>
    <item>
      <title>What is ChromeDriver?</title>
      <dc:creator>tandt53</dc:creator>
      <pubDate>Mon, 19 Jul 2021 02:01:11 +0000</pubDate>
      <link>https://dev.to/tandt53/what-is-chromedriver-2agn</link>
      <guid>https://dev.to/tandt53/what-is-chromedriver-2agn</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;As in previous &lt;a href="https://dev.to/tandt53/selenium-driver-service-1ffc"&gt;post&lt;/a&gt;, we discussed about Driver Service (like ChromeDriverService, FirefoxDriverService, etc.). It is to start and stop Driver Server before WebDriver initialized and after quit. So that, service needs to be configured driver executable file (via method &lt;code&gt;useDriverExecutable(File file)&lt;/code&gt;). When working with selenium, you should have a driver executor for coresponding browser, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome browser requires &lt;code&gt;chromedriver.exe&lt;/code&gt; on Windows, or &lt;code&gt;chromedrive&lt;/code&gt; on Linux and Mac.&lt;/li&gt;
&lt;li&gt;Firefox browser requires &lt;code&gt;geckodriver.exe&lt;/code&gt; on Windows, &lt;code&gt;geckodriver&lt;/code&gt; on Linux and Mac.&lt;/li&gt;
&lt;li&gt;Edge browser requires &lt;code&gt;edgedriver.exe&lt;/code&gt; on Windows, &lt;code&gt;edgedriver&lt;/code&gt; on Mac.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, what are they responsible for? Why do we need it in Selenium web automation?&lt;/p&gt;

&lt;h1&gt;
  
  
  Driver Executor
&lt;/h1&gt;

&lt;p&gt;Before we go further, I would remind the log when we start ChromeDriver. It will start a server on free port or on pre-defined port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting ChromeDriver 91.0.4472.101 (...) on port 5353
ChromeDriver was started successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  First try
&lt;/h2&gt;

&lt;p&gt;Browser drivers are executable, so we can try to run it in command line and check the result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./chromedriver 
Starting ChromeDriver 91.0.4472.101 (...) on port 9515
ChromeDriver was started successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wow, ChromeDriver server was started on port 9515. It was same with using ChromeDriverService in previous post.&lt;br&gt;&lt;br&gt;
From that, we can see that ChromeDriverService get driver executable file and it will start ChromeDriver server internally in selenium code. Then the other services(FirefoxDriverService, EdgeDriverService) do the same way to start browser Server.&lt;/p&gt;
&lt;h2&gt;
  
  
  Find more with --help
&lt;/h2&gt;

&lt;p&gt;Now, we will explore chromedriver by run it with parameter &lt;code&gt;--help&lt;/code&gt; to understand more options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./chromedriver --help
Usage: ./chromedriver [OPTIONS]

Options
  --port=PORT                     port to listen on
  --adb-port=PORT                 adb server port
  --log-path=FILE                 write server log to file instead of stderr, increases log level to INFO
  --log-level=LEVEL               set log level: ALL, DEBUG, INFO, WARNING, SEVERE, OFF
  --verbose                       log verbosely (equivalent to --log-level=ALL)
  --silent                        log nothing (equivalent to --log-level=OFF)
  --append-log                    append log file instead of rewriting
  --replayable                    (experimental) log verbosely and don't truncate long strings so that the log can be replayed.
  --version                       print the version number and exit
  --url-base                      base URL path prefix for commands, e.g. wd/url
  --readable-timestamp            add readable timestamps to log
  --enable-chrome-logs            show logs from the browser (overrides other logging options)
  --allowed-ips                   comma-separated allowlist of remote IP addresses which are allowed to connect to ChromeDriver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are so many options available that driver server can start with.&lt;br&gt;&lt;br&gt;
For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To check chromedriver version, use &lt;code&gt;--version&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./chromedriver --version
ChromeDriver 91.0.4472.101 (...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To start ChromeDriver server with specific port (5353), use &lt;code&gt;--port&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ./chromedriver --port=5353
Starting ChromeDriver 91.0.4472.101 (...) on port 5353
ChromeDriver was started successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>selenium</category>
    </item>
    <item>
      <title>Selenium Driver Service</title>
      <dc:creator>tandt53</dc:creator>
      <pubDate>Mon, 19 Jul 2021 02:00:38 +0000</pubDate>
      <link>https://dev.to/tandt53/selenium-driver-service-1ffc</link>
      <guid>https://dev.to/tandt53/selenium-driver-service-1ffc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this post, I would like to introduce the different ways to initialize instance of WebDriver. It will help you understand more clearly about &lt;a href="https://www.selenium.dev/documentation/en/webdriver/understanding_the_components/"&gt;Selenium Architecture&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post focuses on ChromeDriver constructors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No argument constructor: &lt;code&gt;public ChromeDriver()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Constructor with 1 parameter ChromeDriverService: &lt;code&gt;public ChromeDriver(ChromeDriverService service)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  No argument constructor
&lt;/h2&gt;

&lt;p&gt;Firstly, I created a sample source code for initialize ChromeDriver instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;chromeDriverFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"chromedriver"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// I downloaded chromedriver and put it in the root directory of project. On mac, the executor is chromedriver, but on Windows, using chromedriver.exe instead. &lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"webdriver.chrome.driver"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chromeDriverFilePath&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; 
&lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriver&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://google.com.vn"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above codes will start a selenium session and launch Google Chrome browser. Then it navigate to home page of google search. Let's run programm, we can see the log as follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting ChromeDriver 91.0.4472.101 (...) on port 8945
ChromeDriver was started successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It shows that ChromeDriver was started successfully on port 8945. It means that there is some server was started on my machine on port 8945. But I did not set any thing like port or server options in our code. Where did they come from? Is ChromeDriver a server?&lt;br&gt;
We will answer those question in next session.&lt;/p&gt;
&lt;h2&gt;
  
  
  Constructor with 1 parameter ChromeDriverService
&lt;/h2&gt;

&lt;p&gt;Let's create an instance of ChromeDriverService via Builder and then create ChromeDriver with that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;chromeDriverFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"chromedriver"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;ChromeDriverService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriverService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usingAnyFreePort&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usingDriverExecutable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chromeDriverFilePath&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;ChromeDriverService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://google.com.vn"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code shows that &lt;code&gt;service&lt;/code&gt; will be built after set 2 information for &lt;code&gt;builder&lt;/code&gt; (usingAnyFreePort, and usingDriverExecutable is a file of chromedriver). Then that instance is passed into constructor of ChromeDriver then navigate to Google Search. Now run the program to see the behavior nothing different but log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting ChromeDriver 91.0.4472.101 (...) on port 29284
ChromeDriver was started successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's change a little bit in our code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;ChromeDriverService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriverService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usingPort&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5353&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// change &lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usingDriverExecutable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"chromedriver"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;ChromeDriverService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// new line &lt;/span&gt;
&lt;span class="nc"&gt;WebDriver&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ChromeDriver&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://google.com.vn"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quit&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// new line&lt;/span&gt;
&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// new line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are 4 lines updated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;line 2: we changed builder using any free port by a specific port number (5353).&lt;/li&gt;
&lt;li&gt;line 5: service is started before create instance of ChromeDriver.&lt;/li&gt;
&lt;li&gt;line 7: driver is quit after navigate to Google search.&lt;/li&gt;
&lt;li&gt;line 8: service is stopped after driver quit.
Now run program once again and check the console log:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Starting ChromeDriver 91.0.4472.101 (...) on port 5353
ChromeDriver was started successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, ChromeDriver server was started on port 5353, as we set in &lt;code&gt;builder&lt;/code&gt; of service. Now we understand that ChromeDriverService will be incharged of initiate ChromeDriver server using configured informations (port and driver executable file). And we can control those configuration (starting server in different port, or using different version of chromedriver) as we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;From above code, we see that out test scripts will start &lt;code&gt;an driver server&lt;/code&gt; (ChromeDriver server, GeckoDriver server, etc.) whenever we call new instance of WebDriver for corresponding browser. Then, every command (click, sendKeys, getText, etc.) will be transformed from script (selenium client) to that server to interact with browser, and finaly get result.&lt;/p&gt;

&lt;p&gt;In &lt;a href="//%7B%7B&lt;%20ref%20"&gt;}}"&amp;gt;next&lt;/a&gt; post, I will present about driver executable file (chromedriver.exe, geckodriver.exe) for more details.&lt;/p&gt;

</description>
      <category>selenium</category>
    </item>
  </channel>
</rss>
