Repository navigation
Run the browser tests in CloakBrowser, attached to its vendor's container - #13
Draft
jwrosewell wants to merge 6 commits into
Draft
jwrosewell wants to merge 6 commits into
jwrosewell wants to merge 6 commits into
Conversation
… container CloakBrowser is a patched Chromium published for browser automation. The examples have to work in it as they do in Chrome and Firefox, so it becomes a fourth browser the suite can drive. The browser binary is closed source, so the suite never downloads or starts it. It runs in the vendor's container, and BrowserDrivers.CreateCloak attaches a ChromeDriver to it at the address CLOAK_DEBUGGER_ADDRESS names. The driver has to match the Chromium inside CloakBrowser and not the Chrome on the machine, so the suite reads the version from the browser and has Selenium Manager fetch that driver, or takes the one CLOAKWEBDRIVER names. A chromedriver on the path is never used. The browser outlives the driver, so each attach moves to a new tab, closes the others and clears the cookies and the cache. A test in the Browser category proves that, and fails when the reset is taken out. Every Cloak test carries RequiresCloak, which skips it with a reason naming the variable until the variable is set, so a run that does not set it is unchanged. The skip happens before the initialize method, which matters for the Contract tests, as they start the example there. Tests added: - Browser: Cloak_RunsThePage and Cloak_StartsEachTestClean. - CloudInternal: CloakTests, the counterpart of ChromeTests. - Contract: a _Cloak test beside each existing test. The two client-side override tests emulate no device in CloakBrowser, because ChromeDriver refuses mobile emulation for a browser it attaches to, and work with the screen the browser reports for itself. - Unit tests for the address, the version, the driver lookup and the skip reason.
…category in it A third job starts the vendor's image on ubuntu-latest and runs the Browser category with CLOAK_DEBUGGER_ADDRESS set, so the wiring is proved on every pull request before a language repository relies on it. The image is named by digest, so the browser cannot change under the tag, and its update check is turned off, so the browser that runs is the one in the image. Nothing from the runner is mounted into the container and no secret is passed to it. Host networking lets the browser reach the page the tests serve on localhost. The job looks for the pass of Cloak_RunsThePage by name, because a skipped Cloak test would otherwise leave it green having attached to nothing.
Adds CLOAK_DEBUGGER_ADDRESS and CLOAKWEBDRIVER to the configuration table, and a section on starting the browser in its vendor's container, pinned by digest, on Linux and with Docker Desktop on Windows. It says how the driver is chosen, what differs for a test when the driver attaches to a browser it did not start, and which version of the binary's license applies, linked at the commit that holds that version.
…d overrides The size the page is expected to render is read from the browser, as no device can be emulated in a browser the driver attaches to. The server-side rows can read 0 for a desktop browser, so a browser that reported a screen of 0 would have matched them whether or not the client-side callback ran. The test now fails in that case and says what the browser reported. Run against the .NET cloud example, CloakBrowser reports a screen of 1920 by 1080, the server-side rows read 0 and the client-side rows read 1920 and 1080. With the script's call back to the example refused, this test and the three other CloakBrowser tests that depend on the call back fail.
CloakBrowser is a third party's closed source browser, so a pipeline has to know which keys it is shown. The CloakTests class runs the cloud's JavaScript in the browser, and that script carries the resource key and the license it was requested with. The README and the class now say so, and that a pipeline running CloudInternal under CloakBrowser should use keys it is content for that browser to see. The Contract tests put no key in anything they give the browser and pass on what the example serves.
…er job The CloakBrowser job ran the whole Browser category, so it started Chrome and Firefox as well, which the browsers job already does on the same image. Firefox's start on that image takes from 3 seconds to more than 60, and in one run it passed Selenium's limit of 60 seconds and failed the job although both Cloak tests had passed. The job now runs only the tests that attach to CloakBrowser, which is what it is there to prove. The container is not what slows Firefox. Measured in this repository's CI on the ubuntu-24.04 image 20260927.320.1, Firefox took 3, 19, 31 and more than 60 seconds with the container beside it, and in two of those runs the browsers job, which has no container, took 46 and 48 seconds for Chrome and Firefox together. In a Linux container sharing a network with CloakBrowser, Firefox took 5 to 12 seconds to run the smoke test, and 6 to 13 without it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds CloakBrowser as a fourth browser the suite can drive, beside Chrome, Firefox and Edge, so that the browser tests of every example can also be run in it. CloakBrowser is a patched Chromium that its vendor publishes for browser automation.
Nothing changes for a run that does not opt in. Every new test is skipped, with a reason that names the variable to set, until
CLOAK_DEBUGGER_ADDRESSis set.How it works
The CloakBrowser binary is closed source, so the suite never downloads it and never starts it. The browser runs only inside the vendor's Docker image, in the vendor's server mode (
cloakserve), and the suite attaches a ChromeDriver to it through the DevTools port the container serves.CLOAK_DEBUGGER_ADDRESS127.0.0.1:9222. While it is unset every Cloak test is skipped.CLOAKWEBDRIVERWhat was added, by file:
Helpers/BrowserDrivers.csCreateCloak(ChromeOptions)readsCLOAK_DEBUGGER_ADDRESS, asks the browser for its version at/json/version, finds a chromedriver of that major version, setsChromeOptions.DebuggerAddressand attaches.CLOAKWEBDRIVERnames, or else the one Selenium Manager fetches for that version. A chromedriver on the path is never used, because it belongs to the machine's own Chrome.FindNamedDriveris the lookup that does not search the path, andFindDrivernow calls it before searching the path, with the same result as before.Helpers/RequiresCloakAttribute.csis a test condition that skips a test, or a whole class, whileCLOAK_DEBUGGER_ADDRESSis unset, giving the variable as the reason.Selenium/DriverSmokeTests.cs(categoryBrowser) gainsCloak_RunsThePageandCloak_StartsEachTestClean.Browsers/CloakTests.cs(categoryCloudInternal) is the counterpart ofChromeTests.Contractclasses each gain a_Cloaktest beside the existing ones, beingExample_RendersRealDetectionResult_Cloak,Example_ServesCoreJs_AndClientSideOverridesFlow_Cloak,Example_RendersClientSideOverridesOnThePage_CloakandSessionStorageCache_Cloak. The existing tests keep their names and their assertions.Example_RendersClientSideOverridesOnThePage_Cloakreads the size it expects from the browser, and fails if the browser reports a screen of 0.Helpers/BrowserDriversCloakTests.csholds unit tests for the address, the version, the driver lookup and the skip reason. They start no browser..github/workflows/build-and-test.ymlgains acloakjob, which starts the image by digest onubuntu-latest, runs the Cloak tests of theBrowsercategory attached to it, and fails unlessCloak_RunsThePagepassed.README.mdgains the two variables and a section called Running under CloakBrowser, which also says which keys the tests give the browser.How a consumer opts in
Start the container before the tests. This is the form for a Linux CI runner.
Set
CLOAK_DEBUGGER_ADDRESS=127.0.0.1:9222, and optionallySE_AVOID_BROWSER_DOWNLOAD=true.Run the same
dotnet testcommand as today. The Cloak tests of the chosen category then run beside the Chrome and Firefox ones.Remove the container with
docker rm -f cloak.The
cloakjob in this repository's workflow is a working copy of those steps. With Docker Desktop on Windows the README gives a second form, which publishes the port on the loopback address and mapslocalhostto the host.What was checked and found
Checked on Windows 11 with Docker Desktop (engine 29.7.2), against
cloakhq/cloakbrowser:0.5.12at the digest above, and in a Linux container for the host networking case. Findings 8 to 12 come from running theContractcategory against a real example.cloakserve.cloakserveis a proxy in front of the browser's own DevTools port. ChromeDriver asks it for/json/versionand/json/listand then opens the browser WebSocket, and the proxy passes all three on. ChromeDriver 146.0.7680.165, fetched by Selenium Manager, attached to the browser, which reportsChrome/146.0.7680.177.--headlessand--user-agentin the options are ignored, because the container started the browser. It sends its own user agent,Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36, and it runs with a window on the container's virtual display.unrecognized chrome option: mobileEmulation. A device metrics override sent through DevTools after attaching changeddevicePixelRatioand leftscreen.widthandscreen.heightat 1920 and 1080.CLOAKBROWSER_AUTO_UPDATE=false, the vendor's documented setting, turns that off, and the container log then shows neither request./app/BINARY-LICENSE.mdinside the image has the same SHA-256 (a959b6f9db58f7e273694368659140e9d82960d964ab48b5f6cf9c4545cc2981) as the file at the commit the README links. The digest names an image index that coverslinux/amd64andlinux/arm64.Contracttests pass in CloakBrowser against a real example. The example was the cloud web example of the .NET examples,Examples/Cloud/GettingStarted-Web, pointed athttps://cloud.51degrees.com/with a paid resource key. It was started on its own and given to the suite asEXAMPLE_URL, which is how CI calls the suite. WithCLOAK_DEBUGGER_ADDRESSset all 14 results passed, and without it 9 passed and 5 were skipped. The five CloakBrowser results passed in eight runs out of eight.fod.completeholds the same two numbers. The two tests that emulate no device rest on exactly that.POSTto/51dpipeline/jsonrefused, four of the five CloakBrowser results fail, each on the assertion about what should have come back.Example_RendersRealDetectionResult_Cloakstill passes, because it reads only the page the server renders.Contracttests make a browser load from it was fetched and compared with the resource key, being the page, its stylesheet and scripts,/51Degrees.core.jswith and without the cookie parameter, and the call back to/51dpipeline/json. None carried the key. The script also callshttps://cloud.51degrees.com/api/v4/3pcfrom the browser, and that answer carries no key either. Every run then went through a pass-through in front of the example that would have withheld any answer carrying the key, and across more than 390 answers none did. The control run was repeated straight at the example with the same 9 passes, so the pass-through changes no result.CloakTestsdoes give the browser keys. The class fetches the cloud's JavaScript include itself and runs it in the browser. Asked for the way the class asks, the include carries the resource key and the license it was requested with. The builder that makes the include copies the request's query parameters into the script, which is how the license gets there.CloakTestswas therefore not run in CloakBrowser here, and the README now says which keys a pipeline hands over. The page-basedCloudInternalclasses put a resource key in a script address on their pages, and they drive Chrome, Firefox and Edge only.Where this differs from the first plan, and why
Assert.Inconclusivein the test. TheContractclasses start the example in their initialize method, which runs before the test body. An attribute skips the test before that, so no example is started for a test that cannot use it. The reason printed is the same kind of line the existing skips give.Example_ServesCoreJs_AndClientSideOverridesFlow_Cloakkeeps the assertions that the screen size and the device id came back, and drops the two that check the emulation.Example_RendersClientSideOverridesOnThePage_Cloakexpects the page to render the screen size the browser reports for itself, and fails if that size is 0, because the server-side rows can read 0 and would then match whether or not the call back ran.Example_RendersRealDetectionResult_Cloaksets no user agent, because of finding 3.ChromeOptions.BrowserVersionand leaving the rest to Selenium worked on a machine that has a Chrome, and failed withBrowser not foundin a container with no Chrome whenSE_AVOID_BROWSER_DOWNLOAD=true.CLOAKBROWSER_AUTO_UPDATE=false, because of finding 6.cloakjob runs only the Cloak tests of theBrowsercategory. It first ran the whole category, which starts Chrome and Firefox as well, and in one of four runs Firefox took more than 60 seconds to start and failed the job although both Cloak tests had passed. Nothing found points at the container as the cause. With the container beside it Firefox took 3, 19, 31 and more than 60 seconds, and thebrowsersjob, which has no container, took 46, 48 and 57 seconds for Chrome and Firefox together in three runs on the same evening. In a Linux container sharing a network with CloakBrowser, Firefox took 5 to 12 seconds to run the smoke test, and 6 to 13 without it. Thebrowsersjob starts Chrome and Firefox on the same image, so each browser is still started on every pull request. What this repository's CI no longer does is start Chrome and Firefox beside the container.Verification
Build, with
dotnet build -c Release.Unit set, as this repository's CI runs it, with no variables set.
maingives 39 passed and 15 skipped, so the 18 new results are the new unit tests and the skips are unchanged.Cloak tests of the
Browsercategory on Windows, attached to the container through Docker Desktop.Cloak_StartsEachTestCleanfails when the reset is taken out ofCreateCloak, which is what makes the clean start a checked claim.The same test on Linux, with the vendor container and the suite sharing a host network, no Chrome on the machine and
SE_AVOID_BROWSER_DOWNLOAD=true.With the variable unset every Cloak test is skipped and says why.
With the variable set to an address where nothing listens, the test fails and names the variable and the address.
Contractcategory against the .NET cloud example on Windows, withCLOAK_DEBUGGER_ADDRESSset. The user agent arguments in the test names are trimmed here.The same command with the variable unset, as the control. The nine Chrome and Firefox results pass as above.
The CloakBrowser results with the script's call back refused, which is finding 10.
Example_RendersClientSideOverridesOnThePage_Cloakwith the width read from the browser forced to 0.The
cloakjob ran on a realubuntu-latestrunner (imageubuntu-24.04, version 20260927.320.1) and passed on the latest commit, in this run. It pulled the image by digest, and its log shows these lines.Two earlier runs did not pass first time. In the first, the three
ubuntu-latestjobs were cancelled before any step ran, with "The job was not acquired by Runner of type hosted even after multiple attempts", while GitHub was reporting an incident with Actions, and they passed when re-run. In the second, Firefox took more than 60 seconds to start in thecloakjob, which is why that job no longer starts Firefox.What was not run
CloakTestswas not run in CloakBrowser, on purpose. The script it runs in the browser carries the resource key and the license (finding 12), and there were no keys at hand that are meant to be seen by a third party's browser. The class compiles, is discovered, and is skipped when the variable is unset. Its first run belongs to a pipeline that supplies such keys.Contractruns were made on Windows with Docker Desktop. On Linux with host networking only theBrowsercategory has run, in this repository's CI.Contracttests assert on, which was confirmed with the cloud's accessible properties call before the run, and the example logged five others as not available with that key. No test reads those rows.linux/arm64build of the image were not tried.What remains
CLOAK_DEBUGGER_ADDRESSin its own CI before its examples are tested in CloakBrowser. That is follow-up work in those repositories and is not part of this pull request.Contractunder CloakBrowser, it is worth checking that its example gives the browser no key, as finding 11 did for the .NET cloud example.CloudInternalunder CloakBrowser needs keys it is content for a third party's browser to see.ubuntu-latestimage comes close to Selenium's limit of 60 seconds at times, with no CloakBrowser involved. Thebrowsersjob took 46, 48 and 57 seconds for Chrome and Firefox together in three runs here, against 18 to 34 seconds in four runs in September. That is a risk to thebrowsersjob and to everyContractrun that uses Firefox, and it is not addressed here.ubuntu-latestlabel moves to Ubuntu 26 from 19 October 2026 (runner-images issue 14748), so thecloakjob is worth watching on its first run after that.