Giter VIP home page Giter VIP logo

clj-chrome-devtools's Introduction

clj-chrome-devtools

Clojars Project Tests

clj-chrome-devtools is a simple library for controlling a headless Chrome with the Chrome DevTools Protocol. The protocol is based on a websocket connection between clojure and chrome. All the functions are automatically generated from the protocol specification JSON files.

Quick start with Babashka

See bb.clj file for a quick script that can be used from Babashka.

Goals

The goal of the project is to provide a general purpose library for using headless chrome, both in production use and in testing scenarios.

The CDP part, which is autogenerated, should be stable and will be updated when CDP protocol changes.

The higher level automation utilities is where most of the work is done and new automation utilities are welcome. PRs welcome, just follow the common pattern of providing two arities: one for using the global automation context and one where it is provided as the first parameter.

API Docs

See codox generated API docs.

All the low-level auto-generated commands from Chrome Devtools Protocol are in clj-chrome-devtools.command.* namespaces. Note: to use the low-level API you need to implement event handlers for listening to data Chrome sends the client.

There is the beginnings of a rudimentary higher level API in clj-chrome-devtools.automation. There is also a clojure.test fixture to run tests with a fresh headless chrome in clj-chrome-devtools.automation.fixture.

Example usage

For using the higher level API for screen scraping and browser testing, see chrome_test.clj file.

The following shows a simple REPL usage, navigating to a page and inspecting content. The connection is the first parameter of all calls. You can also set the connection to use with set-current-connection! and omit the connection parameter for convenience.

clj-chrome-devtools.core> (def c (connect "localhost" 9222))
#'clj-chrome-devtools.core/c
clj-chrome-devtools.core> (require '[clj-chrome-devtools.commands.page :as page]
                                   '[clj-chrome-devtools.commands.dom :as dom])
nil
clj-chrome-devtools.core> (page/navigate c {:url "http://webjure.org/"})
{:frame-id "68439.1"}
clj-chrome-devtools.core> (dom/get-document c {:depth 1})
{:root
 {:children
  [{:node-type 1,
    :node-id 2,
    :backend-node-id 4,
    :parent-id 1,
    :node-name "HTML",
    :node-value "",
    :frame-id "68439.1",
    :local-name "html",
    :child-node-count 2,
    :attributes []}],
  :document-url "http://webjure.org/",
  :node-type 9,
  :base-url "http://webjure.org/",
  :node-id 1,
  :backend-node-id 3,
  :node-name "#document",
  :node-value "",
  :xml-version "",
  :local-name "",
  :child-node-count 1}}
clj-chrome-devtools.core> (use 'clojure.repl)
nil
clj-chrome-devtools.core> (doc dom/get-outer-html)
-------------------------
clj-chrome-devtools.commands.dom/get-outer-html
([] [{:as params, :keys [node-id]}] [connection {:as params, :keys [node-id]}])
  Returns node's HTML markup.

Parameters map keys:
  :node-id              Id of the node to get markup for.

Return map keys:
  :outer-html           Outer HTML markup.
nil
clj-chrome-devtools.core> (dom/get-outer-html c {:node-id 1})
{:outer-html
 "<html><head>\n    <title>Webjure</title>\n  </head>\n  <body>\n    Coming soon-ish!\n  \n\n</body></html>"}

Running ClojureScript test suite with clj-chrome-devtools

The clj-chrome-devtools.cljs.test contains a function that can be used to build and run ClojureScript tests as part of your Clojure test without needing doo plugin or karma installed.

See example in Tuck project.

The build-and-test function is meant to be called inside your test and it will build the specified ClojureScript build that is defined in project.clj (e.g. "test") with an added test runner that requires the specified namespaces and runs tests in them.

clj-chrome-devtools's People

Contributors

agilecreativity avatar andyhorng avatar bendlas avatar hagmonk avatar mattiuusitalo avatar maxweber avatar mikkoronkkomaki avatar oneness avatar stathissideris avatar tatut avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

clj-chrome-devtools's Issues

[Feature Request] Some missing functionality compared with Puppeteer

I have been using puppeteer and always wondered to have something like this project in clj world, so thanks a lot!

When I try to port of my project from puppeteer to clj-chrome-devtools, there are essentially several missing pieces that are blocking me:

  1. When connected to a browser, clj-chrome-devtools always use of the first page listed in the CDP websocket endpoint.
  2. Some handy features are missing, e.g. page.title(), page.url() etc.
  3. Querying by XPATH seems not supported, only css selectors are allowed.

Does it make sense to add these supports to this project? If so I may try to contribute to it.

Add useful guide to start the project locally

Thanks for a great library:

I have one suggestion that might be useful for someone to get started with the project:

git clone https://github.com/tatut/clj-chrome-devtools.git
cd clj-chrome-devtools
git submodule init
git submodule update
# then follow the guide in the README to start the REPL
# e.g. 
lein repl 
# or 
lein test

can't add latest git version as dependency

Cloning: https://github.com/tatut/clj-chrome-devtools.git
Checking out: https://github.com/tatut/clj-chrome-devtools.git at a2c07289a55afef7c2ea81bf42cc76c70c9ebc63
Error building classpath. Checkout conflict with file: resources/devtools-protocol
org.eclipse.jgit.api.errors.JGitInternalException: Checkout conflict with file: resources/devtools-protocol

I have the following in my deps.edn:

   :aliases {:test {
                 :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
                                                         :sha "209b64504cb3bd3b99ecfec7937b358a879f55c1"}
                              tatut/clj-chrome-devtools
                                                        {:git/url "https://github.com/tatut/clj-chrome-devtools.git"
                                                         :sha "a2c07289a55afef7c2ea81bf42cc76c70c9ebc63"}
                              }
                 :main-opts ["-m" "cognitect.test-runner"]}}

Can't connect to remote debugging port when launched via `headless? false`

Hey all. Just started exploring this library, thanks for all the awesome work!

So this by alone does not work

(def conn (launcher/launch-automation {:headless? false}))

and results in the following exception after 30s

Execution error (ExceptionInfo) at clj-chrome-devtools.impl.connection/wait-for-remote-debugging-port (connection.clj:74).
Chrome remote debugging port not up

The only workaround I found for this was to first launch with headless? true and then launch another automation with headless? false

So this works properly

;; need this hack to run in GUI
(launcher/launch-automation {:headless? true})
(def conn (launcher/launch-automation {:headless? false}))

Getting blank PDFs when saving `page/print-to-pdf` data as a string

I've gotten clj-chrome-devtools.commands.page/print-to-pdf tantalizingly close to working, but clearly I'm doing something not quite right. Happy to help with steps-to-reproduce or a PR, but I'll need some help shaping these.

Steps to reproduce (maybe):

  • install chrome on ubuntu 20.04
  • run chrome in a shell with google-chrome --disable-gpu --headless --remote-debugging-port=9222
  • connect to chrome in clojure and try to generate a PDF file per
(require
 '[clj-chrome-devtools.core          :as chrome]
 '[clj-chrome-devtools.commands.page :as page])

(defn b64-decode
  "https://stackoverflow.com/a/39188819/53790"
  [to-decode]
  (String. (.decode (Base64/getDecoder) to-decode)))

(def c (chrome/connect "localhost" 9222))
(page/navigate c {:url "https://github.com"})
(let [response (page/print-to-pdf c {:page-ranges "1-2"})
      pdf-data (b64-decode (:data response))]
  (spit "test.pdf" pdf-data))

Expected:

Should get a 2-page letter-size (ie. the default) PDF of the Github site as test.pdf.

Actual:

There is a 2-page letter-size PDF created at test.pdf, but its pages are blank.

Notes:

  • I've tried different URLs and the file size bounces around, suggesting that there's different data in them.
  • I can successfully generate PDFs using (clojure.java.shell/sh "google-chrome" "--headless" "--disable-gpu" "--print-to-pdf=test.pdf" "https://github.com") so I don't believe it's a problem with the Chrome installation.
  • I can successfully use npm puppeteer-core with this Chrome installation and running this script as node test-script.js, so I'm suspecting the problem lies somewhere between Chrome and my code:
const puppeteer = require("puppeteer-core");

(async () => {
  const browser = await puppeteer.connect({
    // from "webSocketDebuggerUrl" at http://127.0.0.1:9222/json/version
    browserWSEndpoint:
      "ws://127.0.0.1:9222/devtools/browser/d6687d16-aabb-4eb5-9f39-38275307c218",
  });
  const page = await browser.newPage();
  await page.goto("https://github.com", {
    waitUntil: "networkidle2",
  });
  await page.pdf({ path: "test.pdf" });
})();

Help wanted : Could you please clarify how to run the test?

Hello guys,

I'm getting the following exception when trying to execute the clj-chrome-devtools.chrome-test/simple-page-load with lein eftest command (or just from IntelliJ Idea / Cursive plugin).

CompilerException java.lang.IllegalArgumentException: Cannot open <nil> as a Reader., compiling:(clj_chrome_devtools/commands/dom.clj:4:1)

Could you please help me to figure it out how can I execute the test example?
Thank you.

Question about the goals of the project

First big ๐Ÿ‘ from me on this repo. I have been doing work with the devtools protocol in JS land recently and daydreaming about using it from Clojure.

I'm curious about your vision for the project. The beginnings of a rudimentary higher level API in clj-chrome-devtools.automation specifically. Is that intended to grow into a full high level API or to serve as quick start examples when building you own higher level interface lib?

root-node-id seems broken

Clicks etc from clj-chrome-devtools.automation seem broken. For example:

(a/start!)
(a/click "div.button")

Throws:

1. Unhandled clojure.lang.ExceptionInfo
   Error in command DOM.querySelector: Invalid parameters
   {:request
    {:id 1,
     :method "DOM.querySelector",
     :params {"nodeId" nil, "selector" "div.button"}},
    :error
    {:code -32602,
     :message "Invalid parameters",
     :data "nodeId: integer value expected"}}
                   dom.clj:    4  clj-chrome-devtools.commands.dom/query-selector
                   dom.clj:    4  clj-chrome-devtools.commands.dom/query-selector
            automation.clj:  179  clj-chrome-devtools.automation/sel1/evaluate--auto--
            automation.clj:  178  clj-chrome-devtools.automation/sel1
            automation.clj:  174  clj-chrome-devtools.automation/sel1

I've traced this down to the fact that the library caches the root-node ID. If monkey-patch root-node-id so that resolves the ID on every call, the code above works!

(in-ns 'clj-chrome-devtools.automation)
(defn- root-node-id
  ([] (root-node-id @current-automation))
  ([ctx]
   (-> ctx :connection (clj-chrome-devtools.commands.dom/get-document {}) :root :node-id)))
(in-ns 'test.browser)

Support running deps.edn/figwheel-main based tests

Current clojure.test integration has support for running cljs tests as part of a clojure test suite and supports leiningen projects.

Add support for building and running tests for deps.edn/figwheel-main based projects.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.