From bb3c7d9f9124fc5cdbb5b6ee55246adf2b20e5da Mon Sep 17 00:00:00 2001 From: Edward Bebbington Date: Thu, 12 Aug 2021 00:29:00 +0100 Subject: [PATCH 1/5] chore: Remove default url when creating a headless client --- src/chrome_client.ts | 13 +++++++++---- src/client.ts | 1 - src/firefox_client.ts | 9 ++------- tests/unit/chrome_client_test.ts | 7 ------- tests/unit/firefox_client_test.ts | 7 ------- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/chrome_client.ts b/src/chrome_client.ts index ed3acee7..0df8ef92 100644 --- a/src/chrome_client.ts +++ b/src/chrome_client.ts @@ -49,14 +49,20 @@ export class ChromeClient extends Client { // FILE MARKER - METHODS - PUBLIC //////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// + /** + * Entry point for creating a headless chrome browser. + * + * @param buildOptions - Any extra options you wish to provide to customise how the headless browser sub process is ran + * - hostname: Defaults to localhost + * - port: Defaults to 9293 + * + * @returns An instance of ChromeClient, that is now ready. + */ public static async build(options: BuildOptions = {}): Promise { // Setup build options if (!options.debuggerPort) { options.debuggerPort = 9292; } - if (!options.defaultUrl) { - options.defaultUrl = "https://chromestatus.com"; - } if (!options.hostname) { options.hostname = "localhost"; } @@ -68,7 +74,6 @@ export class ChromeClient extends Client { "--remote-debugging-port=" + options.debuggerPort, "--disable-gpu", "--no-sandbox", - options.defaultUrl, ]; return await Client.create(args, { hostname: options.hostname, diff --git a/src/client.ts b/src/client.ts index 64ea945f..59b9ce8c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -3,7 +3,6 @@ import { existsSync } from "./utility.ts"; export interface BuildOptions { debuggerPort?: number; // The port to start the debugger on for Chrome, so that we can connect to it. Defaults to 9292 - defaultUrl?: string; // Default url chrome will open when it is ran. Defaults to "https://chromestatus.com" hostname?: string; // The hostname the browser process starts on. If on host machine, this will be "localhost", if in docker, it will bee the container name. Defaults to localhost binaryPath?: string; //The Full Path to the browser binary. If using an alternative chromium based browser, this field is necessary. } diff --git a/src/firefox_client.ts b/src/firefox_client.ts index 399f0155..8c8aaee3 100644 --- a/src/firefox_client.ts +++ b/src/firefox_client.ts @@ -53,7 +53,6 @@ export class FirefoxClient extends Client { * @param buildOptions - Any extra options you wish to provide to customise how the headless browser sub process is ran * - hostname: Defaults to 0.0.0.0 for macos/linux, 127.0.0.1 for windows * - port: Defaults to 9293 - * - url: Defaults to https://developer.mozilla.org/ * * @returns An instance of FirefoxClient, that is now ready. */ @@ -67,11 +66,10 @@ export class FirefoxClient extends Client { if (!buildOptions.debuggerPort) { buildOptions.debuggerPort = defaultBuildOptions.debuggerServerPort; } - if (!buildOptions.defaultUrl) { - buildOptions.defaultUrl = defaultBuildOptions.defaultUrl; - } + // Create the profile the browser will use. Create a test one so we can enable required options to enable communication with it const tmpDirName = Deno.makeTempDirSync(); + // Create the arguments we will use when spawning the headless browser const args = [ buildOptions.binaryPath || getFirefoxPath(), @@ -83,9 +81,6 @@ export class FirefoxClient extends Client { "-profile", tmpDirName, ]; - if (buildOptions.defaultUrl) { - args.push(buildOptions.defaultUrl); - } // Create the sub process to start the browser return await Client.create( args, diff --git a/tests/unit/chrome_client_test.ts b/tests/unit/chrome_client_test.ts index 1843baf5..3b4afcd9 100644 --- a/tests/unit/chrome_client_test.ts +++ b/tests/unit/chrome_client_test.ts @@ -42,13 +42,6 @@ Rhum.testPlan("tests/unit/chrome_client_test.ts", () => { await Sinco.done(); }, ); - Rhum.testCase("Uses the url when passed in to the parameters", async () => { - const Sinco = await ChromeClient.build({ - defaultUrl: "https://drash.land", - }); - await Sinco.assertUrlIs("https://drash.land/"); - await Sinco.done(); - }); Rhum.testCase( "Uses the hostname when passed in to the parameters", async () => { diff --git a/tests/unit/firefox_client_test.ts b/tests/unit/firefox_client_test.ts index 91b12c75..c34d6bbb 100644 --- a/tests/unit/firefox_client_test.ts +++ b/tests/unit/firefox_client_test.ts @@ -43,13 +43,6 @@ Rhum.testPlan("tests/unit/firefox_client_test.ts", () => { await Sinco.done(); }, ); - Rhum.testCase("Uses the url when passed in to the parameters", async () => { - const Sinco = await FirefoxClient.build({ - defaultUrl: "https://drash.land", - }); - await Sinco.assertUrlIs("https://drash.land/"); - await Sinco.done(); - }); Rhum.testCase( "Uses the hostname when passed in to the parameters", async () => { From 4c8e43834a4df0e04d04919efca137e2a33f5524 Mon Sep 17 00:00:00 2001 From: Edward Bebbington Date: Thu, 12 Aug 2021 00:39:50 +0100 Subject: [PATCH 2/5] deno lint --- examples/firefox_client_example.ts | 3 ++- src/client.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/firefox_client_example.ts b/examples/firefox_client_example.ts index 98d3431a..27271e6a 100644 --- a/examples/firefox_client_example.ts +++ b/examples/firefox_client_example.ts @@ -68,7 +68,8 @@ async function simplifiedFirefoxExample() { async function request(type: string, params = {}, name: string): Promise<{ id: number; - // deno-lint-ignore no-explicit-any Any because we return a packet + // because we return a packet + // deno-lint-ignore no-explicit-any message: Record; }> { // Construct data in required format to send diff --git a/src/client.ts b/src/client.ts index 59b9ce8c..eddba962 100644 --- a/src/client.ts +++ b/src/client.ts @@ -405,7 +405,8 @@ export class Client { private async sendWebSocketMessage( method: string, params?: { [key: string]: unknown }, - // deno-lint-ignore no-explicit-any The return value could literally be anything + // The return value could literally be anything + // deno-lint-ignore no-explicit-any ): Promise { const data: { id: number; From c58c9b88f5205f6a12d5e9fdbce7f2f3f32542b5 Mon Sep 17 00:00:00 2001 From: Edward Bebbington Date: Thu, 12 Aug 2021 17:54:54 +0100 Subject: [PATCH 3/5] debug why assert int test wont finish --- src/client.ts | 10 +++ .../assert_methods_clean_up_on_fail_test.ts | 66 ++++++++++--------- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/src/client.ts b/src/client.ts index eddba962..84ae9729 100644 --- a/src/client.ts +++ b/src/client.ts @@ -157,6 +157,7 @@ export class Client { const method = "Page.loadEventFired"; this.notification_resolvables.set(method, deferred()); const notificationPromise = this.notification_resolvables.get(method); + console.log('gotoing') const res = await this.sendWebSocketMessage("Page.navigate", { url: urlToVisit, }) as { @@ -369,6 +370,7 @@ export class Client { ////////////////////////////////////////////////////////////////////////////// private handleSocketMessage(message: MessageResponse | NotificationResponse) { + console.log(message) if ("id" in message) { // message response const resolvable = this.resolvables.get(message.id); if (resolvable) { @@ -462,24 +464,30 @@ export class Client { stderr: "piped", stdout: "piped", }); + console.log('ran subprocess') // Oddly, this is needed before the json/list endpoint is up. // but the ws url provided here isn't the one we need for await (const line of readLines(browserProcess.stderr)) { + console.log(line) const match = line.match(/^DevTools listening on (ws:\/\/.*)$/); if (!match) { continue; } break; } + console.log('getting url') const wsUrl = await Client.getWebSocketUrl( wsOptions.hostname, wsOptions.port, ); + console.log('got url') const websocket = new WebSocket(wsUrl); const promise = deferred(); websocket.onopen = () => promise.resolve(); await promise; + console.log('open') const TempClient = new Client(websocket, browserProcess, browser); + await TempClient.sendWebSocketMessage("Page.enable"); await TempClient.sendWebSocketMessage("Runtime.enable"); return new Client(websocket, browserProcess, browser, firefoxProfilePath); @@ -504,8 +512,10 @@ export class Client { try { const res = await fetch(`http://${hostname}:${port}/json/list`); const json = await res.json(); + console.log(json) debugUrl = json[0]["webSocketDebuggerUrl"]; } catch (_err) { + console.log(_err.message) // do nothing, loop again until the endpoint is ready } } diff --git a/tests/integration/assert_methods_clean_up_on_fail_test.ts b/tests/integration/assert_methods_clean_up_on_fail_test.ts index 01227719..b9f47ce2 100644 --- a/tests/integration/assert_methods_clean_up_on_fail_test.ts +++ b/tests/integration/assert_methods_clean_up_on_fail_test.ts @@ -26,48 +26,50 @@ import { ChromeClient, FirefoxClient } from "../../mod.ts"; // THIS TEST SHOULD NOT HANG, IF IT DOES, THEN THIS TEST FAILS -Deno.test("Chrome: Assertion methods cleanup when an assertion fails", async () => { - const Sinco = await ChromeClient.build(); - await Sinco.goTo("https://chromestatus.com"); - await Sinco.assertUrlIs("https://chromestatus.com/features"); - let gotError = false; - let errMsg = ""; - try { - await Sinco.assertSee("Chrome Versions"); // Does not exist on the page (the `V` is lowercase, whereas here we use an uppercase) - } catch (err) { - gotError = true; - errMsg = err.message - // deno-lint-ignore no-control-regex - .replace(/\x1b/g, "") // or \x1b\[90m - .replace(/\[1m/g, "") - .replace(/\[[0-9][0-9]m/g, "") - .replace(/\n/g, ""); - } - assertEquals(gotError, true); - assertEquals( - errMsg, - "Values are not equal: [Diff] Actual / Expected- false+ true", - ); - // Now we should be able to run tests again without it hanging - const Sinco2 = await ChromeClient.build(); - await Sinco2.goTo("https://chromestatus.com"); - await Sinco2.assertUrlIs("https://chromestatus.com/features"); - try { - await Sinco2.assertSee("Chrome Versions"); - } catch (_err) { - // - } -}); +// Deno.test("Chrome: Assertion methods cleanup when an assertion fails", async () => { +// const Sinco = await ChromeClient.build(); +// await Sinco.goTo("https://chromestatus.com"); +// await Sinco.assertUrlIs("https://chromestatus.com/features"); +// let gotError = false; +// let errMsg = ""; +// try { +// await Sinco.assertSee("Chrome Versions"); // Does not exist on the page (the `V` is lowercase, whereas here we use an uppercase) +// } catch (err) { +// gotError = true; +// errMsg = err.message +// // deno-lint-ignore no-control-regex +// .replace(/\x1b/g, "") // or \x1b\[90m +// .replace(/\[1m/g, "") +// .replace(/\[[0-9][0-9]m/g, "") +// .replace(/\n/g, ""); +// } +// assertEquals(gotError, true); +// assertEquals( +// errMsg, +// "Values are not equal: [Diff] Actual / Expected- false+ true", +// ); +// // Now we should be able to run tests again without it hanging +// const Sinco2 = await ChromeClient.build(); +// await Sinco2.goTo("https://chromestatus.com"); +// await Sinco2.assertUrlIs("https://chromestatus.com/features"); +// try { +// await Sinco2.assertSee("Chrome Versions"); +// } catch (_err) { +// // +// } +// }); Deno.test("Firefox: Assertion methods cleanup when an assertion fails", async () => { const Sinco = await FirefoxClient.build(); await Sinco.goTo("https://chromestatus.com"); + console.log('after goto') await Sinco.assertUrlIs("https://chromestatus.com/features"); let gotError = false; let errMsg = ""; try { await Sinco.assertSee("Chrome Versions"); // Does not exist on the page (the `V` is lowercase, whereas here we use an uppercase) } catch (err) { + console.log('ERR') gotError = true; errMsg = err.message // deno-lint-ignore no-control-regex From 8163ecc9e72e6863a8b301853ae7fe9b9843620a Mon Sep 17 00:00:00 2001 From: Edward Bebbington Date: Thu, 12 Aug 2021 19:20:56 +0100 Subject: [PATCH 4/5] fix issue with firefox hanging --- src/client.ts | 9 --- src/firefox_client.ts | 3 +- .../assert_methods_clean_up_on_fail_test.ts | 66 +++++++++---------- 3 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/client.ts b/src/client.ts index 84ae9729..7772d411 100644 --- a/src/client.ts +++ b/src/client.ts @@ -157,7 +157,6 @@ export class Client { const method = "Page.loadEventFired"; this.notification_resolvables.set(method, deferred()); const notificationPromise = this.notification_resolvables.get(method); - console.log('gotoing') const res = await this.sendWebSocketMessage("Page.navigate", { url: urlToVisit, }) as { @@ -370,7 +369,6 @@ export class Client { ////////////////////////////////////////////////////////////////////////////// private handleSocketMessage(message: MessageResponse | NotificationResponse) { - console.log(message) if ("id" in message) { // message response const resolvable = this.resolvables.get(message.id); if (resolvable) { @@ -464,28 +462,23 @@ export class Client { stderr: "piped", stdout: "piped", }); - console.log('ran subprocess') // Oddly, this is needed before the json/list endpoint is up. // but the ws url provided here isn't the one we need for await (const line of readLines(browserProcess.stderr)) { - console.log(line) const match = line.match(/^DevTools listening on (ws:\/\/.*)$/); if (!match) { continue; } break; } - console.log('getting url') const wsUrl = await Client.getWebSocketUrl( wsOptions.hostname, wsOptions.port, ); - console.log('got url') const websocket = new WebSocket(wsUrl); const promise = deferred(); websocket.onopen = () => promise.resolve(); await promise; - console.log('open') const TempClient = new Client(websocket, browserProcess, browser); await TempClient.sendWebSocketMessage("Page.enable"); @@ -512,10 +505,8 @@ export class Client { try { const res = await fetch(`http://${hostname}:${port}/json/list`); const json = await res.json(); - console.log(json) debugUrl = json[0]["webSocketDebuggerUrl"]; } catch (_err) { - console.log(_err.message) // do nothing, loop again until the endpoint is ready } } diff --git a/src/firefox_client.ts b/src/firefox_client.ts index 8c8aaee3..d9d4cd55 100644 --- a/src/firefox_client.ts +++ b/src/firefox_client.ts @@ -75,11 +75,12 @@ export class FirefoxClient extends Client { buildOptions.binaryPath || getFirefoxPath(), "--start-debugger-server", buildOptions.debuggerPort.toString(), + '-headless', "--remote-debugging-port", buildOptions.debuggerPort.toString(), - "--headless", "-profile", tmpDirName, + "about:blank" ]; // Create the sub process to start the browser return await Client.create( diff --git a/tests/integration/assert_methods_clean_up_on_fail_test.ts b/tests/integration/assert_methods_clean_up_on_fail_test.ts index b9f47ce2..01227719 100644 --- a/tests/integration/assert_methods_clean_up_on_fail_test.ts +++ b/tests/integration/assert_methods_clean_up_on_fail_test.ts @@ -26,50 +26,48 @@ import { ChromeClient, FirefoxClient } from "../../mod.ts"; // THIS TEST SHOULD NOT HANG, IF IT DOES, THEN THIS TEST FAILS -// Deno.test("Chrome: Assertion methods cleanup when an assertion fails", async () => { -// const Sinco = await ChromeClient.build(); -// await Sinco.goTo("https://chromestatus.com"); -// await Sinco.assertUrlIs("https://chromestatus.com/features"); -// let gotError = false; -// let errMsg = ""; -// try { -// await Sinco.assertSee("Chrome Versions"); // Does not exist on the page (the `V` is lowercase, whereas here we use an uppercase) -// } catch (err) { -// gotError = true; -// errMsg = err.message -// // deno-lint-ignore no-control-regex -// .replace(/\x1b/g, "") // or \x1b\[90m -// .replace(/\[1m/g, "") -// .replace(/\[[0-9][0-9]m/g, "") -// .replace(/\n/g, ""); -// } -// assertEquals(gotError, true); -// assertEquals( -// errMsg, -// "Values are not equal: [Diff] Actual / Expected- false+ true", -// ); -// // Now we should be able to run tests again without it hanging -// const Sinco2 = await ChromeClient.build(); -// await Sinco2.goTo("https://chromestatus.com"); -// await Sinco2.assertUrlIs("https://chromestatus.com/features"); -// try { -// await Sinco2.assertSee("Chrome Versions"); -// } catch (_err) { -// // -// } -// }); +Deno.test("Chrome: Assertion methods cleanup when an assertion fails", async () => { + const Sinco = await ChromeClient.build(); + await Sinco.goTo("https://chromestatus.com"); + await Sinco.assertUrlIs("https://chromestatus.com/features"); + let gotError = false; + let errMsg = ""; + try { + await Sinco.assertSee("Chrome Versions"); // Does not exist on the page (the `V` is lowercase, whereas here we use an uppercase) + } catch (err) { + gotError = true; + errMsg = err.message + // deno-lint-ignore no-control-regex + .replace(/\x1b/g, "") // or \x1b\[90m + .replace(/\[1m/g, "") + .replace(/\[[0-9][0-9]m/g, "") + .replace(/\n/g, ""); + } + assertEquals(gotError, true); + assertEquals( + errMsg, + "Values are not equal: [Diff] Actual / Expected- false+ true", + ); + // Now we should be able to run tests again without it hanging + const Sinco2 = await ChromeClient.build(); + await Sinco2.goTo("https://chromestatus.com"); + await Sinco2.assertUrlIs("https://chromestatus.com/features"); + try { + await Sinco2.assertSee("Chrome Versions"); + } catch (_err) { + // + } +}); Deno.test("Firefox: Assertion methods cleanup when an assertion fails", async () => { const Sinco = await FirefoxClient.build(); await Sinco.goTo("https://chromestatus.com"); - console.log('after goto') await Sinco.assertUrlIs("https://chromestatus.com/features"); let gotError = false; let errMsg = ""; try { await Sinco.assertSee("Chrome Versions"); // Does not exist on the page (the `V` is lowercase, whereas here we use an uppercase) } catch (err) { - console.log('ERR') gotError = true; errMsg = err.message // deno-lint-ignore no-control-regex From e36c9fb77e8aaba96ca5027a91ff904e0a2730ac Mon Sep 17 00:00:00 2001 From: Edward Bebbington Date: Thu, 12 Aug 2021 19:26:17 +0100 Subject: [PATCH 5/5] fmt --- src/firefox_client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/firefox_client.ts b/src/firefox_client.ts index d9d4cd55..826b2642 100644 --- a/src/firefox_client.ts +++ b/src/firefox_client.ts @@ -75,12 +75,12 @@ export class FirefoxClient extends Client { buildOptions.binaryPath || getFirefoxPath(), "--start-debugger-server", buildOptions.debuggerPort.toString(), - '-headless', + "-headless", "--remote-debugging-port", buildOptions.debuggerPort.toString(), "-profile", tmpDirName, - "about:blank" + "about:blank", ]; // Create the sub process to start the browser return await Client.create(