-1

Description

Some buttons on my website change color on hover, however when using it would appear to be having no effect.

Relevant code:

import { puppeteer } from './deps.ts';

const browser = await puppeteer.launch();
const page = await browser.newPage();

const url = 'https://sweet-snake-49.deno.dev';
await page.goto(url);
await page.setViewport({
    width: 1600,
    height: 1200
});
await page.hover('#left');
await page.screenshot({ path: Deno.cwd() + '/example.png' }); 

await browser.close();

Complete project to reproduce here.

Expected output

On hover, I expect the color to change to a yellow with box-shadow, but unfortunately it's as if no hover is being attributed to my button.

From:

To:

Question

How can I get page.hover() to work on my #left button ?

It would not appear to be related to deno as the exact same issue is happening with nodejs

kemicofa ghost
  • 16,349
  • 8
  • 82
  • 131

2 Answers2

2

I used a Docker container (because I'm not comfortable with essentially disabling the security sandbox by using the liberal permissions required by deno-puppeteer), so my config is a bit different from yours.

I have modified both the Dockerfile template from the deno-puppeteer repo, and your source file, and I get a successful hover state in the screenshot:

Note that the target page you linked to uses the glyph (U+25C0 BLACK LEFT-POINTING TRIANGLE) for the text inside the left button — and, it doesn't appear to render in the container: the screenshot shows a default box character in place of it. I suppose a compatible font would need to be installed as an additional step in the Dockerfile in order to address this: more debugging would be required.

Controls with left button hovered

In order to keep the code available on Stack Overflow (rather than relying on external hosting), I'll inline the contents of the files in my reproduction directory (I've named it so-70731163 after the ID of your question), along with the console input/output:


./.vscode/settings.json

{
  "deno.enable": true,
  "deno.lint": true,
  "deno.unstable": true,
  "deno.config": "./src/deno.json"
}

./src/deno.json

{
  "compilerOptions": {
    "lib": [
      "deno.window",
      "dom",
      "dom.iterable"
    ]
  }
}

./src/deps.ts

export * as path from 'https://deno.land/std@0.122.0/path/mod.ts';
export {
  assert,
  assertExists,
} from 'https://deno.land/std@0.122.0/testing/asserts.ts';
export {
  default as puppeteer,
  type ElementHandle,
  type ScreenshotOptions,
} from 'https://deno.land/x/puppeteer@9.0.2/mod.ts';

./src/main.ts

import {
  assert,
  assertExists,
  path,
  puppeteer,
  type ElementHandle,
  type ScreenshotOptions,
} from './deps.ts';

// Create data directory if it doesn't already exist
const dataDir = path.resolve('container-data');
await Deno.mkdir(dataDir, {recursive: true});

// Browser launch options required for usage in Docker
// https://github.com/lucacasonato/deno-puppeteer/issues/16#issuecomment-842784232
// https://github.com/lucacasonato/deno-puppeteer/blob/9.0.2/examples/docker.js
const dockerLaunchArgs = [
  '--no-sandbox',
  '--disable-dev-shm-usage',
];

const browser = await puppeteer.launch({args: dockerLaunchArgs});
const page = await browser.newPage();
await page.setViewport({width: 1600, height: 1200});

const url = 'https://sweet-snake-49.deno.dev';
await page.goto(url);

// Get handle for left button (used in next step) and hover it
const leftBtnHandle = await page.$('button#left');
assertExists(leftBtnHandle);
await leftBtnHandle.hover();

// Get handle for controls container (for screenshot in next step)
const getControls = (leftBtn: HTMLButtonElement) => leftBtn.closest('section');
const controlsHandle =
  (await leftBtnHandle.evaluateHandle<ElementHandle>(getControls)).asElement();
assertExists(controlsHandle);

// Instead of using Puppeteer API to save screenshot,
// get raw buffer data for more control
const screenshotOpts: ScreenshotOptions = {encoding: 'binary', type: 'png'};
const pngData = await controlsHandle.screenshot(screenshotOpts);
assert(pngData instanceof Uint8Array);
const screenshotPath = path.join(dataDir, 'controls.png');
await Deno.writeFile(screenshotPath, pngData);

await browser.close();

./docker-build

docker build --tag so-70731163 .

./docker-run

docker run -it \
  --rm \
  --name so-70731163 \
  --mount type=bind,source="$(pwd)",destination=/app \
  so-70731163

./Dockerfile

FROM debian:buster-slim

ENV DENO_VERSION=1.18.0
ARG DEBIAN_FRONTEND=noninteractive

RUN apt-get -qq update \
    && apt-get -qq install -y --no-install-recommends \
    curl \
    ca-certificates \
    unzip \
# ↓ https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix
# Since I want to leave the contents of troubleshooting.md as it is, ca-certificates is intentionally duplicated here.
    ca-certificates \
    fonts-liberation \
    libappindicator3-1 \
    libasound2 \
    libatk-bridge2.0-0 \
    libatk1.0-0 \
    libc6 \
    libcairo2 \
    libcups2 \
    libdbus-1-3 \
    libexpat1 \
    libfontconfig1 \
    libgbm1 \
    libgcc1 \
    libglib2.0-0 \
    libgtk-3-0 \
    libnspr4 \
    libnss3 \
    libpango-1.0-0 \
    libpangocairo-1.0-0 \
    libstdc++6 \
    libx11-6 \
    libx11-xcb1 \
    libxcb1 \
    libxcomposite1 \
    libxcursor1 \
    libxdamage1 \
    libxext6 \
    libxfixes3 \
    libxi6 \
    libxrandr2 \
    libxrender1 \
    libxss1 \
    libxtst6 \
    lsb-release \
    wget \
    xdg-utils \
# ↑ https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix
# ↓ Added based on the information obtained from by console.log(line) at https://deno.land/x/puppeteer@9.0.2/src/deno/BrowserRunner.ts#L168.
    libdrm2 \
    libxkbcommon0 \
    libxshmfence1 \
# ↑ Added based on the information obtained from by console.log(line) at https://deno.land/x/puppeteer@9.0.2/src/deno/BrowserRunner.ts#L168.
    && curl -fsSL https://github.com/denoland/deno/releases/download/v${DENO_VERSION}/deno-x86_64-unknown-linux-gnu.zip \
    --output deno.zip \
    && unzip deno.zip \
    && rm deno.zip \
    && chmod 755 deno \
    && mv deno /usr/bin/deno \
    && apt-get -qq remove --purge -y \
    curl \
# Do not remove ca-certificates as it is required by puppeteer.
#    ca-certificates \
    unzip \
    && apt-get -y -qq autoremove \
    && apt-get -qq clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN useradd --uid 1993 --user-group deno \
 && mkdir /deno-dir/ \
 && chown deno:deno /deno-dir/

ENV DENO_DIR /deno-dir/

# --- PLACE CUSTOM COMMANDS BELOW --- #

WORKDIR /app
# Bind CWD instead of copying
# COPY . .

# https://deno.land/x/puppeteer@9.0.2#installation
# In your real script, replace the installation script with https://deno.land/x/puppeteer@9.0.2/install.ts
RUN PUPPETEER_PRODUCT=chrome deno run -A --unstable https://deno.land/x/puppeteer@9.0.2/install.ts

ENTRYPOINT ["deno"]
CMD ["run", "-A", "--no-check", "--unstable", "./src/main.ts"]


In the console:

so-70731163 % ./docker-build
# Lots of Docker build output messages...

so-70731163 % ./docker-run
# Lines showing compilation info: Download moduleURL...

and afterward, the screenshot from above is written to ./container-data/controls.png.

To remove the built docker image:

docker image rm so-70731163
jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • Thanks, I was unfortunately not available to review the answer in detail by the time the bounty ended. I'll be rewarding this one. – kemicofa ghost Apr 17 '22 at 12:10
0

You should wait for the element to exist, then try to interact with the element once found in the DOM.

Example using Puppeteer's page waitForSelector:

import { puppeteer } from './deps.ts';

const browser = await puppeteer.launch();
const page = await browser.newPage();

const url = 'https://sweet-snake-49.deno.dev';

await page.goto(url);
await page.setViewport({
    width: 1600,
    height: 1200
});

// wait for element before trying to hover
page.waitForSelector('#left').then(() =>
    await page.hover('#left');
    await page.screenshot({ path: Deno.cwd() + '/example.png' }); 
);

await browser.close();
CJ Ratliff
  • 333
  • 1
  • 2
  • 11