Issue “Attempted to use detached Frame” using puppeteer and puppeteer-cluster

So, I am building an express.js application that uses puppeteer and pupeteer-cluster for accessing one ukrainian test platform. The thing that I want to do is to solve test using this tools.

So, the flow is like this:

  1. Enter user cabinet
  2. Search for the test
  3. Enter the test
  4. Solve it.

The problem appears when I already have entered the test, answered a few questions and, on some point I am getting an error like this: Attempted to use detached Frame 'D78184744F4CA70F50307B66DDF53250'. My error says that there is a problem getting an element named with a class “.v-test-questions-title” when it’s for sure must be there.

async solveSingleAnswerQuestion() {
        const pageElement = new PageElement(this.page)
        const question = await pageElement.getContent('.v-test-questions-title') -> error says the problem is here

        const variants = await pageElement.getContents(
            '.v-test-questions-radio-block',
            '.v-test-questions-checkbox-block'
        )
        console.log({ question, variants })
        const answerIndex = await generateResponseIndex(
            question || '',
            variants
        )
        console.log({ answerIndex })

        const variantsElements = await pageElement.getElements(
            '.v-test-questions-radio-block',
            '.v-test-questions-checkbox-block'
        )
        await variantsElements[answerIndex - 1].click()

        const submitButton = await pageElement.getElement('.v-blue-button-test')

        await submitButton.click()

        await waitFor(5000)
    }

Below is the code for PageElement (class that makes all the code cleaner)

import { cleanString, waitFor } from '@/utils'
    import type { ElementHandle, Page } from 'puppeteer'
    
        export class PageElement {
            constructor(private readonly page: Page) {}
        
            async loadAllElements() {
                const pageElement = new PageElement(this.page)
                const button = await pageElement.getElement('.vo-btn-blue')
        
                if (button) {
                    await button.click()
                    await waitFor(5000)
        
                    await this.loadAllElements()
                }
            }
        
            async getElementsByUniqueSelector(selector: string, fallback?: string) {
                const elements = await this.getElements(selector, fallback)
        
                const filteredElements = (
                    await Promise.all(elements.map(this.isElementWithSingleClass))
                ).filter((element) => element !== null)
        
                return filteredElements
            }
        
            async getContentsByUniqueSelector(selector: string, fallback?: string) {
                const elements = await this.getElementsByUniqueSelector(
                    selector,
                    fallback
                )
        
                const contents = await Promise.all(
                    elements.map((element) => element.evaluate((el) => el.textContent))
                )
        
                return this.cleanContents(contents)
            }
        
            async getContents(selector: string, fallback?: string) {
                const elements = await this.getElements(selector, fallback)
        
                const contents = await Promise.all(
                    elements.map((element) => element.evaluate((el) => el.textContent))
                )
        
                return this.cleanContents(contents)
            }
        
            async getElements(selector: string, fallback?: string) {
                const elements = await this.waitForAndGetElements(selector)
        
                if (elements.length > 0) {
                    return elements
                }
        
                if (fallback) {
                    return await this.waitForAndGetElements(fallback)
                }
        
                return []
            }
        
            async getContent(selector: string, fallback?: string) {
                const element = await this.getElement(selector, fallback)
        
                const content = await element?.evaluate((el) => el.textContent)
                const cleanedContent = cleanString(content || '')
        
                return cleanedContent
            }
        
            async getElement(selector: string, fallback?: string) {
                const elements = await this.getElements(selector, fallback)
        
                return elements[0]
            }
        
            private async waitForAndGetElements(selector: string) {
                const el = await this.page
                    .waitForSelector(selector, {
                        visible: true,
                        timeout: 5000,
                    })
                    .catch(() => null)
        
                const elContent = await el?.evaluate((el) => el.textContent)
                console.log({ elContent })
        
                return await this.page.$$(selector) -> error says that the problem is here. Puppeteer can't get the ".v-test-questions-title" element
            }
        
            private async isElementWithSingleClass(element: ElementHandle) {
                const className = await element.getProperty('className')
                const classList = (await className.jsonValue()).toString().split(' ')
        
                return classList.length > 1 ? null : element
            }
        
            private async cleanContents(contents: (string | null)[]) {
                return contents.map((content) => cleanString(content || ''))
            }
        }

Also, my cluster initialization

import { Cluster } from 'puppeteer-cluster'

export const cluster = await Cluster.launch({
    concurrency: Cluster.CONCURRENCY_CONTEXT,
    maxConcurrency: 1000,
    // monitor: true,
    puppeteerOptions: {
        headless: true,
        dumpio: true,
        args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
            '--disable-dev-shm-usage',
        ],
    },
})

And this is the service code

async solveTestByTitle(testTitle: string) {
        await this.enterCabinet()

        const test = new Test(this.page)
        const pageElement = new PageElement(this.page)
        await test.startSolvingTestByTitle(testTitle)

        const question = await pageElement.getContent('.v-test-questions-title')
        console.log({ questionInService: question })

        while (question) {
            await test.solveSingleAnswerQuestion()
        }
    }

I have been trying a lot of things like changing puppeteer options, running code in the container, changing the browser, changing the code, using page.waitForNavigation(), page.waitForNetworkIdle in some places, nothing helped, but some of the approaches just changed the error name.

Worth noticing that when a bot answers the question, url does not change.

Two independent scales on one chart D3

I have a line chart created in d3 (but drawn with Skia, since it’s for mobile app using react native, I use d3 for calculations, scales etc.), which has 2 lines on it. Both of the lines have different scales (range of the first one is quite large, something like [50000, 60000], the second one has a much smaller range, something like [-0.15, 0.6]).

I’m facing a problem with having a nice y axes for both of them. My current solution is that I have one “primary” scale for line1, which has nice numbers on it, eg. [50k, 52.5k, 55k, 57.5k, 60k] and the axis for my “secondary” scale is generated this way:

I want both yAxes to have numbers in the same spots (the same y point) to draw one set of ref lines on the chart for both of them, so for each number I show on primary axis, I take it’s y position and check what number is there on my secondary scale, then I use this number. The problem with this solution is, that for my secondary scale numbers aren’t nice on the axis, they could be eg. [-0.155, 0.015, 0.185, 0.355, 0.525].

I would like to make both of my axes have nice number and most importantly, to always have 0 for my secondary scale on y axis, but for this I need to somehow create both of them together, rather than separately, so that they are aligned.

Of course just using .nice() from d3 won’t work here. It generates 2 nice scales, but it doesn’t help, if I need them aligned on y axis.

I know, that d3 doesn’t have a functionality like this, already searched the internet for it. I have seen some solutions to align scales, if they both start from 0, but it’s not the case here.
I also know that it’s possible, since eg. HighCharts is able to achieve it, but since it’s not open source, I can’t check how they do it.

Do you know how can I achieve it?

JavaScript page visibility event isn’t being triggered in Python Selenium tests

My website has a feature that sends data when the page visibility changes to “hidden.” It works fine during manual testing but doesn’t trigger in automated testing with Python Selenium. I’ve also tried using the Page Lifecycle API with the same outcome.

Here’s the code snippet:

document.onvisibilitychange = () => {
    if (document.visibilityState === "hidden") {
        sendBeaconData();
    }
};

Is there a possibility to adjust the JavaScript code so it’s detected during automated testing, without making changes to the Selenium setup?

Note: pageunload and similar functions aren’t viable solutions, as they don’t work consistently on mobile devices.

I have tried Page Lifecycle API and pagehide event but it provides same result.

https://developer.chrome.com/docs/web-platform/page-lifecycle-api#advice-hidden

Puppeteer Cannot Find Chromium in DigitalOcean App Platform’s Custom Path

I’m trying to run a Node.js/Express application that uses Puppeteer to generate PDFs on DigitalOcean App Platform. Despite installing Chromium through an .aptfile, I’m encountering issues with launching the browser.

Environment

Platform: DigitalOcean App Platform
Node.js version: 20.x
Puppeteer version: Latest
Deployment: Using .aptfile for dependencies

Code
Here’s my Express endpoint that uses Puppeteer:

import type { Request, Response } from "express";
import type { InvoiceRequest } from "./types/invoiceRequest";
import * as pug from "pug";
import puppeteer from "puppeteer";
import express from "express";
import fs from "fs";

const app = express();
const port = 3000;

// Precompile the Pug template once at startup
const compiledFunction = pug.compileFile("./templates/invoice.pug");

// Optional timeout middleware
app.use((req, res, next) => {
  res.setTimeout(60000, () => {
    res.status(504).json({ error: "Request timed out." });
  });
  next();
});

// Middleware to parse JSON bodies
app.use(express.json());

app.get("/", (req: Request, res: Response) => {
  res.send("Hello World!");
});

app.post(
  "/invoice/generate-invoice",
  async (req: Request<{}, {}, InvoiceRequest>, res: Response) => {
    console.log("Received request:", req.body);
    try {
      const invoiceData = req.body;

      // Calculate totals
      const subtotal = invoiceData.products.reduce(
        (sum, product) => sum + product.price,
        0
      );
      const vat = subtotal * 0.25; // 25% VAT
      const total = subtotal + vat;

      // Format dates
      const invoiceDate = new Date().toISOString().split("T")[0];
      const dueDate = new Date(Date.now() + 14 * 24 * 60 * 60 * 1000)
        .toISOString()
        .split("T")[0];

      // Generate HTML using Pug template
      const html = compiledFunction({

      });

      // Use Puppeteer to generate the PDF from HTML
      const browser = await puppeteer.launch({
        args: [
          "--no-sandbox",
          "--disable-setuid-sandbox",
          "--disable-dev-shm-usage",
          "--single-process",
          "--no-zygote",
        ],
        executablePath: "/layers/digitalocean_apt/apt/usr/bin/chromium-bsu",
      });
      const page = await browser.newPage();
      await page.setContent(html, { waitUntil: "networkidle0" });
      const pdfBuffer = await page.pdf({ format: "A4" });
      await browser.close();

      res.setHeader("Content-Type", "application/pdf");
      res.setHeader("Content-Disposition", "attachment; filename=invoice.pdf");
      res.send(pdfBuffer);
      // store the pdf localy
      fs.writeFile("invoice.pdf", pdfBuffer, (err) => {
        if (err) {
          console.error("Error saving invoice:", err);
        } else {
          console.log("Invoice saved successfully");
        }
      });
    } catch (error) {
      console.error("Error generating invoice:", error);
      res.status(500).json({ error: "Failed to generate invoice" });
    }
  }
);

app.listen(process.env.PORT || port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

This is my Aptfile:

chromium
chromium-common
chromium-sandbox
ca-certificates
fonts-liberation
libappindicator3-1
libasound2
libatk-bridge2.0-0
# ... [other dependencies] ...

This is the Error i am getting in digital ocean:

Error generating invoice: Error: Browser was not found at the configured executablePath (/layers/digitalocean_apt/apt/usr/bin/chromium-bsu)
at ChromeLauncher.launch (/workspace/node_modules/puppeteer-core/lib/cjs/puppeteer/node/BrowserLauncher.js:76:19)

Retaining text selection underneath container element

I’m trying to develop a browser extension that will allow users to select HTML tags on a page using the mouse (like browser inspect mode), but I have problems with implementation. Using scripts, I add the following structure to the page.

<div class="container">
    <div class="item"></div>
</div>
  • container – wrapper for all elements

  • item – element that will add a stroke around the selected element on the page (will be displayed above the main element)

.container {
    position: absolute;
    z-index: 1000000;
}

.item {
    border: 1px solid red;
}
document.addEventListener("mouseover", e => {
    selectElement(e.target);
});
document.addEventListener("mouseout", e => {
    unselectElement(e.target);
});

I tried to implement this functionality via mouseover and mouseout events and ran into a problem. Because of the wrapper on top of the page, I can’t select items on the page. After searching for solutions, I found the following options:

  • add pointer-events: none property to the entire container and content
  • hide the container using visibility: none when moving the mouse, then get the element by coordinates and return the previous visibility state

The first method is not suitable because we have direct access to page events on hover, which can cause tooltips or other windows to appear. This will interfere with the functionality of selecting elements.

The second method is not suitable because of poor performance. If we try to use event mousemove, then when moving the cursor quickly, it will not be able to keep up with the update of item.

Is there any possibility to leave the wrapper and make the selection of elements on the page so that it does not affect performance much?

How to make a plugin for Discord (like vencord and betterDiscord) [closed]

Me and my developer are having a tricky time figuring out how we can make an add-on to Discord We’re solely making a simple Discord plugin that could benefit user experience. It is not a client, or discord modification app similar to Vencord and BetterDiscord. It is just going to add a few little buttons to the actual Discord app (on macos and windows)

We know it’s something to do with electron and js injecting but we really need a bit of a lift off as we dont know where to start when it comes to coding and actually testing our plugin on the Discord application.

How do we actually run and test our app, and make it work?

We’ve tried a few things, we’ve looked through the source code of betterDiscord and dmed developers but no one can help us.

We really just want to make a simple plugin to benefit user experience on discord.

How to add click event handler on an edge node of partition chart in amCharts V5

I’m using amCharts V5 in my angular project.
I use partition chart as a kind of a filter. So that when one clicks on edge node (the last node that does not have any children) I will draw another chart underneath.
My problem at this moment is to make click working. There is no explicit example in docs but I assumed that this should work

series.nodes.template.events.on("click", function (event) {
        console.log("Clicked on node:", event);
      });

and here’s how I create my series

      let series = container.children.push(
        am5hierarchy.Partition.new(root, {
          singleBranchOnly: false,
          orientation: this.chartData.orientation,
          downDepth: 1,
          initialDepth: 2,
          valueField: 'value',
          categoryField: 'name',
          childDataField: 'children',
          interactive: true,
          cursorOverStyle: "pointer",
          exportable: true,
          interactiveChildren: true
        })
      );

I don’t even find anything in docs saying that it’s not possible. Please someone help me solve this issue.

Highlight JS code inside XML element or inside CDATA in VS code

I want to have JS syntax highlighting inside certain XML elements. E.g.

 <Module Name="Main">
<![CDATA[
   var obj = {};
   obj.car = { Name: 'car', HP: 135};
   obj.drive = function(car){...}
]]></Module>

This currently shows up as

image

Is there any extension that enables highlighting of the JS inside the element or do i have to do something custom?

I tried all kinds of XML extensions, none seem to have that integrated.

V8 JavaScript engine

I’ve read some articles about V8 .However, I still have Two questions in my head :

Q1– In V8 JavaScript engine : primitive values (like numbers and strings) and references to objects are always stored in the stack, while objects (including arrays and functions) are always stored in the heap but when (the references to objects or primitive values) is a property of another object it (the references to objects or primitive values) is stored in the heap to make the object properties together . is that 100% acurate ?

For example :

  let name ="Tom"; // name is stored in the stack
    let test ={name :"Tom", age:44}; // name and age are stored in the heap and test (the reference to the object) is stored in the stack
    let temp ={t1:test}; // t1 is stored in the heap and temp (the reference to the object) is stored in the stack

Q2this article is about Shapes ( a.k.a Hidden Classes ) and there is that code

const point = {};
point.x = 4;
point.y = 5;
point.z = 6;

and there is a photo in the article that represents how the object (point) is look like in the memory :
enter image description here

I think this photo only represents ( contains ) the part that is on the heap ( in another words : this photo does not represent ( contain ) the reference to the object (point) and that reference is stored in the stack ) so the photo must be like this :
enter image description here

So my second question is : is my thought right ( in another words : is my photo right ? ) ?

How can I make it so that after displaying, the club name it will become clickable?

So I’m working on a “Club Finder” application with my Computer Sci Club but I’m not familiar that much with JavaScript, can you please help?

This is the code:

//We're using an object for our data structure.  There is not limit to the number of clubs and interests we can add
const clubs = [
    { name: "Computer Science Club", interests: ["programming", "technology", "web development"] },
    { name: "Academic World Quest", interests: [""]},
    // Add more clubs and their associated interests
  ];

//Below you will find the JavaScript code for our club page's functionality

  // Function to filter clubs based on selected interests
  function findClubs() {
    const selectedInterests = Array.from(document.querySelectorAll('input[name="interest"]:checked'))
      .map(el => el.value);
  
    const matchingClubs = clubs.filter(club => {
      return club.interests.some(interest => selectedInterests.includes(interest));
    });
  
    displayClubs(matchingClubs);
  }
  
  // Function to display the matching clubs
  function displayClubs(clubs) {
    const resultsContainer = document.getElementById("results");
    resultsContainer.innerHTML = "";
  
    if (clubs.length === 0) {
      resultsContainer.innerHTML = "<p>No matching clubs found.</p>";
    } else {
      const clubList = document.createElement("ul");
      clubs.forEach(club => {
        const listItem = document.createElement("li");
        listItem.textContent = club.name;
        clubList.appendChild(listItem);
      });
      resultsContainer.appendChild(clubList);
    }
  }

SSR: Cannot use import statement outside a module

I am trying to run a Server-Side Rendering web app on Vite. I used the following commands:

npx create vite@latest
  (ssr-react)
  (Non-Streaming)
  (JavaScript)

I installed react-tabs-scrollable and am trying to implement them onto my app. But even just the act of importing them is causing an error.

App.jsx (See Line 4)

import "./App.css";
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import { Tab } from "react-tabs-scrollable";

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vite.dev" target="_blank">
         ...
}

export default App;

But when I try to run the app, it gives me an error:

Error:

[MyFolderPath]sample-projectnode_modulesreact-tabs-scrollabledistreact-tabs-scrollable.es.js:1
import k, { useRef as hr, useCallback as pr, useEffect as _r, useLayoutEffect as gr } from "react";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (node:internal/modules/cjs/loader:1378:20)
    at Module._compile (node:internal/modules/cjs/loader:1428:41)
    ...

.

What I Have Tried:

I have tried changing my package.json to have type: “module”, but it is already there:

package.json

{
  "name": "sample-project",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "node server",
    "build": "npm run build:client && npm run build:server",
    ...

The type=”module” is also present in index.html.

I have also tried using –experimental-modules on the “node server” command. I have tried changing the server file to cjs and mjs, but neither of those worked.

Can I ask how would one fix this situation? Thank you!

How to disable tinymce editor html auto format

This is so customized as I’m using the community version. I have this use case that I have a tinymce on top and bottom while html drag and drop in the middle.

top:
tinymce, content: <div class="test">

middle:
drag and drop html

bottom:
tinymce, content: </div> <!-- closing tag for class .test -->

Tinymce editor will accept only opening tag or only closing tag. I tried other options given from stackoverflow and nothing is working for my use case. As I tried adding a closing tag on source code plugin after I click save button, the closing tag is removed and only the comment remains. While adding an opening tag, it will automatically adding a closing tag.

The actual result that I want to achieve is to retain the opening tag or closing tag only if added on the tinymce editor using the code plugin or any open source plugin.

I used the following technologies below:
jQuery 3.7.1
Bootstrap 5.3
Tinymce 6.7.0

How to get the “space before” flag of PDF’s with AFNumber_Format?

We use the AFNumber_Format to obtain the number information for text fields in PDFs. This allows us to distinguish which number style should be used for the display or which currency symbol should be displayed and whether this symbol should be displayed BEFORE or AFTER the number. Adobe has some flags for this that can be set in their programme. Here you can define the ‘symbol position’. Possible options are ‘before with space’, ‘before without space’, ‘after with space’ and ‘after without space’. However, the flag in AFNumber_Format only specifies a Boolean value (before or after). My question now is: ‘How can I read this with or without spaces from the AFNumber_Format?’

Redirect to Django View after AJAX GET request with form prefilled data

I have a ListView to render page with table of objects. The first column are checkboxes to perform some actions on multiple objects (not a formset). Each checkbox contains an object id as value and utilizing AJAX requests to perform such actions.
There is no issues with direct POST request as I send bunch of ids in array, validate them through the form and update().

However I’m experiencing some difficulties with GET request which suppose to be simplier:

I have a view to edit object (FBV). The goal is to redirect to that view with field contains all those ids checked on ListView. But I just came to know that I can’t call render() after AJAX call so I’m wondering what is the right way to handle such a redirect passing initial data to form presents in that view’s context.

Simplified code:

class ProducutsListView(ListView):
    model = models.Product
    template_name = "production/products_list.html"
    paginate_by = 50

The view to redirect to:

def product_update_view(request):
    if request.method == "POST":
        form = forms.ProductUpdateForm(
                    request.POST,
                )
        products = form.cleaned_data["products"]
        -------------------------------------
        # some more validated data extracting
        -------------------------------------
        products.update(**fields)
        return redirect("products_list")
    else:
        form = forms.ProductUpdateForm()

        if is_ajax(request):
            products_form = forms.ProductsMultipleForm(request.GET) # form contains only one field "poducts" represents an array of objects
            if products_form.is_valid():
                products = products_form.cleaned_data["products"]
                form = forms.ProductUpdateForm(
                    initial={"products": products}
                )
                **# how to render a page with form contains `init` data?**

        return render(request, "production/product_update.html", {"form": form})

JS / AJAX:

// there are multiple Map() objects each contains **view url**, **button** triggers request and **method** of request.
var $buttonsList = [asTested, setOnStock, configChange];
    $.each($buttonsList, function () {
        var $url = this.get("url");
        var $btn = this.get("btn");
        var $method = this.get("method");
        $btn.on("click", function () {
            $.ajax({
                type: $method,
                headers: $method == "POST" ? { "X-CSRFToken": CSRF } : {},
                url: $url,
                dataType: "json",
                traditional : true,
                data: {"products": getProductIds()},
                success: function (response) {
                    if ($method == "POST") {
                        location.reload();
                    };    
                },
            });