Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document common use-case examples #9

Open
andrewvy opened this issue Aug 18, 2017 · 2 comments
Open

Document common use-case examples #9

andrewvy opened this issue Aug 18, 2017 · 2 comments
Milestone

Comments

@andrewvy
Copy link
Owner

Should benefit by providing some common use-case examples.

These examples are built on unstable version and is not guaranteed to be working in the near future!


Taking a screenshot, quick and dirty, error-prone

defmodule CriExample do
  require Logger

  def init() do
    server = ChromeRemoteInterface.Session.new()
    {:ok, pages} = ChromeRemoteInterface.Session.list_pages(server)
    List.first(pages)
  end

  def take_screenshot(page, url, file_path) do
    start_time = System.monotonic_time()
    {:ok, page_pid} = ChromeRemoteInterface.PageSession.start_link(page)

    ChromeRemoteInterface.PageSession.subscribe(page_pid, "Page.frameStoppedLoading")
    ChromeRemoteInterface.RPC.Page.enable(page_pid)
    ChromeRemoteInterface.RPC.Page.navigate(page_pid, %{url: url})

    receive do
      {:chrome_remote_interface, "Page.frameStoppedLoading", _payload} ->
        {:ok, %{"result" => %{"data" => data}}} = ChromeRemoteInterface.RPC.Page.captureScreenshot(page_pid)
        binary_data = Base.decode64!(data)
        {:ok, file} = File.open(file_path, [:write])
        IO.binwrite(file, binary_data)
        File.close(file)

        stop_time = System.monotonic_time()
        diff = System.convert_time_unit(stop_time - start_time, :native, :micro_seconds)

        Logger.info("url=#{url} file_path=#{file_path} total_time=#{formatted_diff(diff)}")
        ChromeRemoteInterface.PageSession.stop(page_pid)
    after
      10_000 ->
        :error
    end
  end

  defp formatted_diff(diff) when diff > 1000, do: [diff |> div(1000) |> Integer.to_string, "ms"]
  defp formatted_diff(diff), do: [Integer.to_string(diff), "µs"]
end
CriExample.init()
|> CriExample.take_screenshot("https://google.com", "test.png")
@andrewvy andrewvy added v1 and removed v1 labels Aug 22, 2017
@andrewvy andrewvy modified the milestone: v1.0.0 Aug 22, 2017
@johns10
Copy link

johns10 commented Nov 23, 2021

Here's another one I came up with, opening chrome and adding a script to evaluate on new pages (took me a while to figure it out):


  def open_chrome(css) do
    task = Task.async(fn ->
      System.cmd(Paths.chromium_executable_path, Constants.chrome_startup_args())
    end)
    server = ChromeRemoteInterface.Session.new()
    {:ok, pages} = ChromeRemoteInterface.Session.list_pages(server)
    pages
    |> Enum.each(fn(page) ->
      {:ok, page_pid} = ChromeRemoteInterface.PageSession.start_link(page)
      Page.enable(page_pid)
      script = script(css)
      Page.addScriptToEvaluateOnNewDocument(page_pid, %{source: script})
    end)
    {task, server}
  end

  def script(css_text) do
  """
  var style = document.createElement('style');
  style.type = 'text/css';
  style.innerHTML = '#{css_text}'
  var script = document.createElement('script');
  script.type = 'text/javascript'
  script.src = 'http://localhost:4001/assets/annotations.js'
  document.addEventListener('DOMContentLoaded', () => {
    var head = document.getElementsByTagName('head')[0]
    head.appendChild(style);
    head.appendChild(script);
  }, false);
  """
  end

@johns10
Copy link

johns10 commented Jan 4, 2022

Here's an example of using evaluate to fetch a remote object and using callfunctionon to operate on the object:


  def get_remote_object("css", selector, page_pid) do
    case Runtime.evaluate(page_pid, %{expression: "document.querySelector('#{selector}');"}) do
      {:ok, %{"result" => %{"result" => %{"subtype" => "null", "type" => "object", "value" => nil}}}} ->
        {:warning, "Element Not Found in Browser"}
      {:ok, %{"result" => %{"result" => remote_object}}} -> {:ok, cast_remote_object(remote_object)}
    end
  end
  def get_remote_object("xpath", selector, page_pid) do
    opts = %{
      includeCommandLineAPI: true,
      expression: "elements = $x('#{selector}'); elements[0];"
    }
    case Runtime.evaluate(page_pid, opts) do
      {:ok, %{"result" => %{"result" => %{"type" => "undefined"}}}} -> {:warning, "Element Not Found"}
      {:ok, %{"result" => %{"result" => remote_object}}} -> {:ok, cast_remote_object(remote_object)}
    end
  end

  def cast_remote_object(%{
    "className" => class_name,
    "description" => description,
    "objectId" => object_id,
    "subtype" => subtype,
    "type" => type
  }) do
    %{
      class_name: class_name,
      description: description,
      object_id: object_id,
      subtype: subtype,
      type: type
    }
  end
  def execute_command({:fill_field, %{strategy: strategy, selector: selector, text: text}}, page_pid) do
    with {:ok, remote_object} <-
        Utilities.get_remote_object(strategy, selector, page_pid),
      {:ok, %{"result" => _focus_result}} <-
        DOM.focus(page_pid, %{objectId: remote_object.object_id}),
      :ok <- type_text(page_pid, text),
      {:ok, %{"result" => result}} <-
        Runtime.callFunctionOn(page_pid, %{
          arguments: [%{objectId: remote_object.object_id}],
          functionDeclaration: "(element) => {element.blur()}",
          objectId: remote_object.object_id
        })
    do
      {:ok, result}
    else
      {:error, error_object} -> cast_error(error_object)
      {:warning, message} -> {:warning, message}
    end
  end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants