Trying to get the list of courseWork of Classroom API with javascript (error “the required parameter courseId is empty”)

I am trying to get the list of courseWork for a specific course. I access an iteration of the courses and when the appropriate course arrives successfully I use the following code to get the courseWork list:

await gapi.client.classroom.courses.courseWork.list(idcourse, 
  {}).then(function(response2) 
{                            
 });

The error I get is that “the required parameter courseId is empty”.

The response2 object is undefined.

idcourse is a string that is obtained in the iteration and I have verified that it is a valid and correct string.

I have tried other options like:

idcourse is a string that is obtained in the iteration and I have verified that it is a valid and correct string.

I have tried other options like:

 await gapi.client.classroom.courses.courseWork.list( 
          {courseId : idcourse}).then(function(response2) 
        {                            
         });

Siempre arroja el mismo error.

How to Create Dual Bars with Hollow Max and Solid Current Values in Chart.js?

I’m trying to create a grouped bar chart using Chart.js, where each group has two bars (e.g., green and red). For each bar:

  1. The solid part represents the current value.
  2. The hollow outline represents the maximum value.

The goal is to overlay the max value as a hollow bar around the current value. Each group should only display two bars (one for each metric, e.g., green and red). However, I keep ending up with four bars per group instead of two.

What I’ve Tried:

  1. Adjusting the dataset structure.
  2. Using custom plugins to overlay hollow bars for max values.
  3. Referenced Chart.js documentation and examples.
  4. Tried implementing solutions with ChatGPT, but they didn’t work as expected.

Desired Output:

Each group should show two bars:

  • Green bar: Solid current value with a hollow outline for the max value.
  • Red bar: Solid current value with a hollow outline for the max value.

expected output

Issue:

issue

Any guidance, working examples, or corrections would be greatly appreciated!

Here’s my code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chart.js Grouped Bar Chart</title>
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <style>
    canvas {
        max-width: 50%;
    }
  </style>
</head>
<body>
  <main>
    <canvas id="barChart"></canvas>
  </main>

  <script>
    const data = {
      labels: ['2020', '2021', '2022', '2023', '2024'],
      datasets: [
        {
          label: 'Current Value (Green)',
          data: [50, 150, 20, 40, 150],
          backgroundColor: 'rgba(0, 255, 0, 0.5)',
          borderColor: 'rgba(0, 255, 0, 1)',
          borderWidth: 0,
          barThickness: 20,
        },
        {
          label: 'Max Value (Green)',
          data: [100, 200, 30, 50, 200],
          backgroundColor: 'rgba(0, 0, 0, 0)', // Transparent fill for hollow effect
          borderColor: 'rgba(0, 255, 0, 1)',
          borderWidth: 2,
          barThickness: 20,
        },
        {
          label: 'Current Value (Red)',
          data: [20, 100, 160, 25, 85],
          backgroundColor: 'rgba(255, 0, 0, 0.5)',
          borderColor: 'rgba(255, 0, 0, 1)',
          borderWidth: 0,
          barThickness: 20,
        },
        {
          label: 'Max Value (Red)',
          data: [50, 150, 180, 30, 100],
          backgroundColor: 'rgba(0, 0, 0, 0)', // Transparent fill for hollow effect
          borderColor: 'rgba(255, 0, 0, 1)',
          borderWidth: 2,
          barThickness: 20,
        },
      ],
    };

    const options = {
      responsive: true,
      maintainAspectRatio: false,
      plugins: {
        legend: {
          display: true,
        },
      },
      scales: {
        x: {
          stacked: false,
          grid: {
            display: false,
          },
        },
        y: {
          beginAtZero: true,
          grid: {
            drawBorder: false,
          },
        },
      },
    };

    const ctx = document.getElementById('barChart').getContext('2d');
    new Chart(ctx, {
      type: 'bar',
      data: data,
      options: options,
    });
  </script>
</body>
</html>

Vue Plugin via CDN: “Cannot read properties of undefined (reading ‘ref’)” Error

I am building a Vue plugin that works fine when installed via npm but throws an error when imported dynamically via a CDN:Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'ref')
I suspect this issue arises because the plugin is using a different Vue instance than the host application.

Plugin Details

  • Vue version in host app: 3.5.13
  • Plugin build: Vite with vue externalized in vite.config.js:
      export default defineConfig({
          build: {
            lib: {
              entry: './src/index.js',
              name: 'NoteWriterPlugin',
              fileName: (format) => `noteWriter.${format}.js`,
              formats: ['es', 'umd'],
            },
            rollupOptions: {
              external: ['vue'],
              output: {
                globals: {
                  vue: 'Vue',
                },
              },
            },
          },
        });
  • Usage: I load the plugin dynamically like this:
import { createApp } from "vue";
import App from "./App.vue";
import loadPluginFromCDN from "./install.js";

const app = createApp(App);

const cdnUrl = "http://127.0.0.1:3000/dist/noteWriter.umd.js";

(async () => {
  const plugin = await loadPluginFromCDN(cdnUrl);
  if (plugin && typeof plugin.install === "function") {
    app.use(plugin);
  } else {
    console.error("The plugin does not expose a valid 'install' method.");
  }

  app.mount("#app");
})();

export default async function loadPluginFromCDN(url) {
  return new Promise((resolve, reject) => {
    const script = document.createElement('script');
    script.src = url;
    script.onload = () => {
      // Ensure the global variable matches the name defined in your UMD build
      console.log(window.NoteWriterPlugin)
      resolve(window.NoteWriterPlugin);
    };
    script.onerror = () => {
      reject(new Error(`Failed to load script: ${url}`));
    };
    document.head.appendChild(script);
  });
}

Debugging Observations

  • The plugin exports an install function and appears to load correctly from the CDN:
  export default {
  install(app) {
    const count = ref(0); // Error occurs here
    app.provide("count", count);
  },
};

  • The error suggests that ref is undefined, indicating that the plugin and host app are using different Vue instances.

Steps Taken

  1. I ensured vue is externalized in the plugin build (external: [‘vue’]).
  2. Both the host app and the plugin are using Vue version 3.5.13 (checked via npm list vue).
  3. When bundled via npm, the plugin works as expected, but loading via CDN results in the error.

Question

How can I ensure that my plugin shares the same Vue instance as the host app when loaded via a CDN? Is there a better way to handle this situation or pass the Vue instance to the plugin dynamically?

Extracting Owner’s Username from Nested Page

I am scraping the HuggingFace research forum (https://discuss.huggingface.co/c/research/7/l/latest) using Selenium. I am able to successfully extract the following attributes from the main page of the forum:

  • Activity Date
  • View Count
  • Replies Count
  • Title
  • URL

However, I am encountering an issue when trying to extract the owner’s username from the individual topic pages. The owner’s username is located on a nested page that is accessible via the URL found in the main page’s topic link.

For example, on the main page, I have the following HTML snippet for a topic:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

import time

# Set up Chrome options to use headless mode (for Colab)
chrome_options = Options()
chrome_options.add_argument("--headless")  # Run in headless mode
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--disable-gpu")
chrome_options.add_argument("--window-size=1920,1080")
chrome_options.add_argument("--disable-infobars")
chrome_options.add_argument("--disable-popup-blocking")
chrome_options.add_argument("--ignore-certificate-errors")
chrome_options.add_argument("--incognito")
chrome_options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36")
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option("useAutomationExtension", False)

# Set the path to chromedriver explicitly (installed by apt)
chrome_path = "/usr/bin/chromedriver"

# Initialize the WebDriver with the updated path
driver = webdriver.Chrome(options=chrome_options)

# Open the HuggingFace page
url = "https://discuss.huggingface.co/c/research/7/l/latest"  # URL for HuggingFace Issues
driver.get(url)

# Wait for the page to load
time.sleep(6)

def scrape_huggingface_issues():
    titles_and_links = []
    seen_titles_and_links = set()
    owner = []
    replies = []
    views = []
    activity = []

    while True:
        try:
            # Find all issue rows (elements in the table)
            elements = driver.find_elements(By.CSS_SELECTOR, 'tr.topic-list-item')

            # Extract and store the titles, links, and other data
            for elem in elements:
                topic_id = elem.get_attribute("data-topic-id")
                if topic_id in seen_titles_and_links:
                    continue

                seen_titles_and_links.add(topic_id)

                # Extract title and link
                selected_title = elem.find_element(By.CSS_SELECTOR, 'a.title.raw-link.raw-topic-link')
                title = selected_title.text.strip()
                relative_link = selected_title.get_attribute('href')  # Get the relative URL from the href attribute
                full_link = relative_link  # Construct the absolute URL (if needed)

                # Extract replies count
                try:
                    replies_elem = elem.find_element(By.CSS_SELECTOR, 'button.btn-link.posts-map.badge-posts')
                    replies_count = replies_elem.find_element(By.CSS_SELECTOR, 'span.number').text.strip()
                except:
                    replies_count = "0"

                # Extract views count
                try:
                    views_elem = elem.find_element(By.CSS_SELECTOR, 'td.num.views.topic-list-data')
                    views_count = views_elem.find_element(By.CSS_SELECTOR, 'span.number').text.strip()
                except:
                    views_count = "0"

                # Extract activity (last activity)
                try:
                    activity_elem = elem.find_element(By.CSS_SELECTOR, 'td.num.topic-list-data.age.activity')
                    activity_text = activity_elem.get_attribute('title').strip()
                except:
                    activity_text = "N/A"

                # Use the helper function to get the owner info from the topic page
                owner_text = scrape_issue_details(relative_link)

                # Store the extracted data in the lists
                titles_and_links.append((title, full_link, owner_text, replies_count, views_count, activity_text))
                seen_titles_and_links.add((title, full_link))  # Add to the seen set to avoid duplicates

            # Scroll down to load more content (if the forum uses infinite scroll)
            driver.find_element(By.TAG_NAME, "body").send_keys(Keys.END)
            time.sleep(3)  # Adjust based on loading speed

            # Check if the "Next" button is available and click it
            try:
                next_button = driver.find_element(By.CSS_SELECTOR, 'a.next.page-numbers')
                next_button.click()
                time.sleep(3)  # Wait for the next page to load
            except:
                # If there's no "Next" button, exit the loop
                print("No more pages to scrape.")
                break

        except Exception as e:
            print(f"Error occurred: {e}")
            continue

    return titles_and_links

def scrape_issue_details(url):
    """
    Navigate to the topic page and scrape additional details like the owner's username.
    """
    # Go to the topic page
    driver.get(url)
    time.sleep(3)  # Wait for the page to load

    # Extract the owner's username
    try:
        owner_elem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'span.first.username.new-user')))
        owner_username_fetch = owner_elem.find_element(By.CSS_SELECTOR, 'a').text.strip()
        owner_username = owner_elem.text.strip()  # Extract the username from the link
    except Exception as e:
        owner_username = "N/A"  # Default value if no owner found

    return owner_username

# Scrape the HuggingFace issues across all pages
issues = scrape_huggingface_issues()

# Print the titles, links, and additional data (owner, replies, views, activity)
print("Scraped Titles, Links, Owner, Replies, Views, Activity:")
for i, (title, link, owner_text, replies_count, views_count, activity_text) in enumerate(issues, 1):
    print(f"{i}: {title} - {link} - Owner: {owner_text} - Replies: {replies_count} - Views: {views_count} - Activity: {activity_text}")

# Close the browser
driver.quit()

Problem:

I cannot fetch the owner’s username from the individual topic page. After following the URL, I am unable to locate and extract the owner’s username even though I know its location in the HTML.

<a href="/t/model-that-can-generate-both-text-and-image-as-output/132209" role="heading" aria-level="2" class="title raw-link raw-topic-link" data-topic-id="132209">Model that can generate both text and image as output</a>

The owner’s username is located on the topic’s individual page at the following HTML snippet:

<span class="first username new-user"><a href="/u/InsertOPUsername" data-user-card="InsertOPUsername" class="">InsertOPUsername</a></span>

What I’ve Tried:

  • I used driver.get(url) to navigate to the individual topic pages.
  • I attempted to locate the username using WebDriverWait and the correct CSS selector (span.first.username.new-user a).
  • I am successfully scraping other details like Activity, Views, and Replies from the main page but unable to retrieve the owner’s username from the topic page.

Is there is any way to reverse the obfuscation code

I am developing a web app that contains the javascript files. I want my javascript file to be safe so I decided to obfuscate it. Is this safe or could someone reverse the obfuscation?

I want to know if there is any tool that could be be used to reverse the obfuscation of a javascript file.

Rails async check for uniqueness

I have a multistage registration for users for a somewhat complicated app. I need to be able to use js to check for unique usernames, etc because users will get pissed off having to redo a multistage form. What is the simplest way to use js to query ActiveRecord?

UI is not updating the index in Angular child component when trying to change it from the parent component. What am I missing?

I’m trying to update the page index from the parent component in an Angular application using a child component that handles pagination. The child component has an index input property and an event emitter for page changes. However, when I try to update the index from the parent, the UI does not reflect the updated value, and the pagination does not change.My project is on Angular 13.

    import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnChanges, SimpleChanges, ChangeDetectorRef } from '@angular/core';

@Component({
  selector: 'numbered-pagination',
  templateUrl: './numbered-pagination.component.html',
  styleUrls: ['./numbered-pagination.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class NumberedPaginationComponent implements OnChanges {
  @Input() index: number = 1;
  @Input() totalCount: number = 0;
  @Input() pageSize: number = 50;
  @Input() rulerLength: number = 5;

  @Output() page: EventEmitter<number> = new EventEmitter<number>();

  maxPages: number = 1;

  ngOnChanges(changes: SimpleChanges): void {
    if (changes['totalCount'] || changes['pageSize']) {
      this.calculateMaxPages();
    }
  }

  private calculateMaxPages(): void {
    this.maxPages = Math.ceil(this.totalCount / this.pageSize) || 1;
  }

  get pagination(): NumberedPagination {
    const { index, maxPages, rulerLength } = this;
    const pages = ruler(index, maxPages, rulerLength);
    return { index, maxPages, pages };
  }

  navigateToPage(pageNumber: number): void {
    if (allowNavigation(pageNumber, this.index, this.maxPages)) {
      this.index = pageNumber;
      this.page.emit(this.index);
      console.log("Index  =>> " + this.index);
    }
    console.log("Index1  =>> " + this.index);
  }

  trackByFn(index: number): number {
    return index;
  }

  constructor(private cdr: ChangeDetectorRef) {}

  refreshToInitial() {
    this.navigateToPage(1);
    this.cdr.detectChanges();
    this.cdr.markForCheck();
  }
}

Parent Component TypeScript

<button class="edit_button" (click)="onAssignToButtonClick('Extracted')">
  Assign To
</button>

<numbered-pagination [index]="extractedPageNumber" (page)="pageEvent('New', $event)" [pageSize]="pageSize" [totalCount]="pageNumberExtractedLimit"></numbered-pagination>

Problem:

  1. I’m trying to update the page index from the parent component using
    the refreshPaginationToInitial method, but the child component’s
    pagination UI doesn’t reflect the updated index.
  2. The child component uses ChangeDetectionStrategy.OnPush and is not
    updating the UI when the index value changes in the parent.

What I’ve tried:

  1. I’m using ChangeDetectorRef in the child component to manually
    trigger change detection, but it doesn’t seem to update the UI.

  2. The child’s pagination logic correctly emits page changes via the
    page output, but the UI doesn’t react to the changes.

Can someone explain why this isn’t working or suggest a solution?

How to turn bun.js async call into sync?

I need to turn async function running in the bun.js engine into sync one.

Reason: large codebase depends on existing sync implementation of the getData function. It needs to be evolved and utilise some async features, but its signature should be kept sync for backward compatibility.

It’s impossible to do with JS. The way I see how to implement it:

  • The main JS process starts WebWorker in background that listen on net port.
  • The getData uses execSync to call curl to send request to WebWorker port, to call async function getDataNewWay and get back the response.

Probably the simplest solution, but the overhead is large.

Wonder if there’s better way? Like:

  • Using unix named pipes and readFileSync/writeFileSync?
  • Using bun ffi to call some C/C++ system library, to do the networking call (same as with execSync and curl, but more efficient)?
  • There are advanced C node.js extensions like fibers that do exactly that, but I would like to avoid heavily altering the JS engine, as it may cause unexpected bugs.
  • XMLHttpRequest in sync mode (seems like it’s not supported in bun)?

Example:

// The API `getData` that should be kept backward compatible
function getData(key: string): string {
  if (key == 'old_key') return getDataOldWay(key)
  return callSync('getDataNewWay', [key])
}

// The possible implementation
function getDataOldWay(key: string): string {
  return 'some data'
}
async function getDataNewWay(key: string): Promise<string> {
  return 'some data'
}
globalThis.getDataNewWay = getDataNewWay

function callSync<T>(fname: string, args: any[]): any {
  const code = `await globalThis.${fname}(...${JSON.stringify(args)})`
  return child_process.execSync(`http://localhost:3000/eval=${code}`).toString()
}

PWA : Navigation bar color [ANDROID]

Can I change the color of the navigation bar in a progressive web app?

This question was asked in 2019 and the answer was that it wasn’t possible. I’m asking this question five years later to see if things have changed.

As you can see from the images below, for a “native” application like youtube the navigation bar is white, whereas in my application it’s black. This seems to be a the default behavior (I test my app with Pixel 9 device) and I would have liked to know if there was a way to influence it with a meta parameter in the html or perhaps by tinkering with a trick with some CSS/JS.

PS1: In my manifest.json file I use display: “standalone” because I want to keep the status bar, so I’m not considering switching to display: “fullscreen”.

PS2: I know this subject has been dealt with many times but the feeds are getting years and on this subject I think things are moving fast.

Navigation bar style with MyApp using Pixel 9 device
Navigation bar style with YouTube using Pixel 9 device

Text disappears after changing focus when typing into walmart.com search bar using JavaScript and Python

I am building a web-scrapper to monitor prices for a few things that go on sale every now and then.

So far I have the following function. I found the search bar element and can send text into it. However the text does not “stick”. Whenever I click on the background of the site, or even the textbox itself and change focus, the text disappears.

Why is this? I can physically see the text in the search bar being input, but it just disappears and cannot be entered in. What am I doing wrong?

def type_in_searchbar(websocket_url, search_term):
    # Type the search term into the Walmart search bar
    try:
        ws = create_connection(websocket_url)

        script = f"""
        var searchBar = document.querySelector('input[aria-label="Search"]');
        if (searchBar) {{
            searchBar.focus();
            searchBar.value = '{search_term}';
        }}
        """

        command = {
            "id": 1,
            "method": "Runtime.evaluate",
            "params": {
                "expression": script
            }
        }

        ws.send(json.dumps(command))
        ws.close()
        print(f"Typed '{search_term}' into the search bar!")
    except Exception as e:
        print(f"Error typing in the search bar: {e}")

Is consulting Chat GPT a good practice for learning?

I am still self-teaching and I am currently developing a browser-based game in javascript. My goal is to learn something after my struggle to solve whatever bugs caused by either logic flaw or my lack of knowledge in the syntax or libraries — only then will I use chat gpt or google.

problem is I feel cheating when I do that, that’s why I won’t give up unless I couldn’t really solve that challenge I have put myself in. I take some small breaks or a nap and then continue where I left off — and that’s my routine. I would forgive myself if I’m making a program for a client, however I am still self-studying.

do you think I should refrain from using chat GPT for checking if my logic was correct? because chat GPT would give me the accurate solution most of the time.

Thank you!

Manual control of SpreadsheetApp.flush()

I’ve written an Apps Scriptfunction, func() that moves data within multiple sheets, however Apps Script is bundling the operations in a way not optimal for user experience. Essentially what happens is that it is bundling all of thesetValue() operations up until the third sheet.getrange() call, in this case sheet3.getRalgeList.getRange(). I noticed this in testing by undoing the script operation and observing that more than one undo in google sheets is required to revert the changes. I want only one undo to be required to undo all changes brought on by this function. Calling SpreadsheetApp.flush() at the end does not resolve this issue. I’ve written other similar functions for my sheet and have never seen this issue before.

How can I fix this issue so that when the function is called, it can be undone with only one undo within Sheets? Right now, when the function is run, two undos are required to revert the changes. It’s evident that this has something to do with the way Apps Script is bundling operations. Is there a way to control this here?

function func() {
  let thing2Load = "Acc1"; 
  
  const sheet1          = func1().sheet;
  const sheet2      = func2().sheet;
  const sheet3         = func3().sheet;
  const var1       = _col_one();
  const var2    = _col_two();
  const var3        = _col_three();
  const var4      = _num_elements1();
  const var5     = _col_three();
  const var6   = _num_elements2();
  const var7              = start().startRow;
  const range1          = range1().range;
  const range2     = range2().range;
  const range3      = range3().range;
  const range4     = range4().range;
  const range5  = range5().range;
  const range6     = range6().range;
  const range7         = range7().range;
  const var9           = thingVals().length;
  const var10          = sheet1.getRange(range4).getValue();
  const var11               = sheet1.getRange(fields().inflows).getValues();
  const var12          = sheet1.getRange(range2).getValue();
  const var13          = sheet2.getRange(range3).getValues();
  const var14            = sheet3.getRange(range7).getValues();
  let var15                = sheet1.getRange(range1).getValues().filter(account => account[0] != "");
  let var16              = var15.map(row => row[0]).indexOf(thing2Load);
  let var17  = sheet2.getRange(var7, var5, sheet2.getMaxRows() - var7 + 1, var6 * var9).getValues();
  let var18    = sheet3.getRange(var7, var3, sheet3.getMaxRows() - var7 + 1, var4 * var9).getValues();
 
  const thingone2Load       = var15[var16][1];
  const thingtwo2Load     = var15[var16][2];
  const thingthree2Load   = var15[var16][3];
  const thingfour2Load    = var17.map(row => row.splice(var16 * var6, var6));
  const thingfive2Load         = var18.map(row => row.splice(var16 * var4, var4));

  var15.splice(var16, 1); 
  var15.push([var12, var10, var11[0], var11[2]]);
  for (let i = 0; i < var17.length; i++) { var17[i].push(...var13[i]); }
  for (let i = 0; i < var18.length; i++) { var18[i].push(...var14[i]); }

  const vals = [thing2Load, thingone2Load, thingtwo2Load, thingthree2Load, var15];
  const test = sheet1.getRange(var1, var2, var15.length, var15[0].length).getA1Notation();

  sheet1.getRangeList([range2, range4, range5, range6, test]).getRanges().forEach((range,idx) => {
      if (idx > 3) { range.setValues(vals[idx]) }
      if (idx <= 3) { range.setValue(vals[idx]); }
  });

  const vals1 = [thingfour2Load, var17];
  const test1 = sheet2.getRange(var7, var5, var17.length, var17[0].length).getA1Notation();

  sheet2.getRangeList([range3, test1]).getRanges().forEach((range,idx) => { range.setValues(vals1[idx]) });

  const vals2 = [thingfive2Load, var18];
  const test2 = sheet3.getRange(var7, var3, var18.length, var18[0].length).getA1Notation();

  sheet3.getRangeList([range7, test2]).getRanges().forEach((range,idx) => { range.setValues(vals2[idx]) });
  SpreadsheetApp.flush();

  return "<span style="font-weight: bold">" + '"'+  thing2Load + "" Thing Loaded</span>";
}

How can I get every URL on my site with Astro including dynamic pages

I was working on a 404 page for my site and I wanted to add a did you mean link. I had already figured out how to find similar page URLs and how to transfer the list of all the URLs from the server to the client, so now I only need to know how to get this list of URLs. Not only that, but I have already tried using import.meta.glob but that doesn’t resolve dynamic pages, which I would like to do if I can.

Chrome controlled by Chromedriver silenced alert popup

I used the following html to test an alert pop up. If a user fills the form, but then switches URL, instead of submitting the form, the page will pop up an alert

<!DOCTYPE html>
<html>

<body>
    <div class="container">
        <form action="#">
            <label>First Name</label>
            <input type="text" id="fname" name="firstname" onchange="updateFname()">
            <button class="submit-btn" onclick="return false;">
                Submit
            </button>
        </form>
    </div>
    <script type="text/javascript">
        let fname = '';
        updateFname = () => {
            fname = document
                .getElementById('fname').value;
        }
        window.addEventListener('beforeunload',
            function (e) {
                if (fname !== '') {
                    console.log("form is filled but not submitted yet");
                    e.preventDefault();
                    e.returnValue = '';
                }
            });
    </script>
</body>

</html>

When I tested the page in chrome alone, the alert popped up as expected
chrome alone

But when I run Selenium with ChromeDriver and Chrome, Chrome didn’t pop up the alert and switched the URL directly.

chrome under chromedriver

The old Chrome and Chromedriver (version under 100 worked fine. Alert popped up).
My Chrome and Chromedirver version is 131.

Is there a way to make the alert pop up again?

Thank you

How to remove ‘with’ from this method

Here is the function in question:

/**
* @el [Element] Target HTML Element 
* @attr [String] attribute to bind
* @match [Object] Result from regex.exec
*/ 
const bindValue = (el, attr, match) => {
  let binding = getBinding(el, attr);

  if (attr === "textContent") {
    let fn = new Function(
      `scope`,
      `with(scope) {
        return ${match[1]}
      }`,
    );

    binding.attrs[attr].bindings.push((ctx, values) => {
      return ctx.replace(match[0], fn(values));
    });
  }
};

So, what this is doing is taking a string from an HTML template for a web component, like:

<div>{{name == 'Homer' ? 'No Homers Allowed" : name}}</div>

and translating it into an executable function within the scope of the component’s bound properties and methods.

In order to not re-render the whole template whenever any bound property is set, during the first render I’m tracking which template binding is being rendered, and using getters on the bound properties to keep track of which bindings are calling which properties, so I can call only those bindings again when that property is set.

What that means is that I need the getter functions to run only when they’re called from within the function created by the Function constructor, which means that they cannot be passed as individual variables to the Function constructor, otherwise the getters for the whole scope run, instead of just those referenced in the template string.

As far as I know, the with statement is the only way to create local variables from either an Object or Array of values other than eval, which is an even worse way of doing it than by using with. This seems like probably the only legitimate use of with, since it’s being used basically to create the scope of a generated function, not replace the scope within an existing function, but it’s still deprecated. I’d like to replace it, if possible, but I can’t figure out a way to do so without sacrificing either the power or simplicity of the template format.

For context, here’s the full code of the web component extension I’m working on which the previous bit is taken from:

class Component extends HTMLElement {
  constructor() {
    super();
    this.__connected__ = false;
    // this.__useShadow__ = false;

    // Binding cache
    this.__bindings__ = [];
    this.__observed__ = {};
    this.__values__ = {};

    this.__children__ = [];

    // Binding utilities
    this.__current_target__ = null;
  }

  static get observedAttributes() {
    return [];
  }

  static get observedProperties() {
    return [];
  }

  listen(key, handle, fn) {
    if (this.__observed__.hasOwnProperty(key)) {
      let observers = this.__observed__[key];
      let idx = observers.findIndex((o) => o.el === this && o.attr === handle);

      if (idx === -1) {
        observers.push({
          el: this,
          attr: handle,
          context: "",
          bindings: [
            (context, values) => {
              return fn(values[key]);
            },
          ],
        });
      } else {
        observers.splice(idx, 1, {
          el: this,
          attr: handle,
          context: "",
          bindings: [
            (context, values) => {
              return fn(values[key]);
            },
          ],
        });
      }
    }
  }

  unlisten(key, handle, fn) {
    if (this.__observed__.hasOwnProperty(key)) {
      let observers = this.__observed__[prop];
      let idx = observers.findIndex((o) => o.el === this && o.attr === handle);

      if (idx !== -1) {
        observers.splice(idx, 1);
      }
    }
  }

  connectedCallback() {
    const name = this.tagName.toLowerCase();

    if (!this.__connected__) {
      this.__connected__ = true;

      // if (this.__useShadow__) {
      //   if (typeof __template__ !== "undefined") {
      //     let template = __template__.content.cloneNode(true);

      //     const shadowRoot = this.attachShadow({ mode: "open" });
      //     shadowRoot.appendChild(template);
      //   }
      // } else {

      this.__children__ = [...this.children];
      if (!!ComponentRegistry.templates[name]) {
        let template =
          ComponentRegistry.templates[name].content.cloneNode(true);

        this.__parseTemplate__(template);
        this.appendChild(template);
      }

      // console.log(this, this.constructor.observedProperties);
      this.constructor.observedProperties.map((prop) => {
        // if (!this.hasOwnProperty(prop)) {
        this.__observed__[prop] = [];

        var p = {};
        p[prop] = {
          get: () => {
            if (
              this.__current_target__ !== null &&
              this.__observed__.hasOwnProperty(prop)
            ) {
              let { el, attr, context, bindings } = this.__current_target__;
              let observers = this.__observed__[prop];
              let idx = observers.findIndex(
                (o) => o.el === el && o.attr === attr,
              );

              if (idx === -1) {
                observers.push({ ...this.__current_target__ });
              } else {
                observers.splice(idx, 1, { ...this.__current_target__ });
              }
            }

            return this.__values__[prop];
          },
          set: (value) => {
            if (value !== this.__values__[prop]) {
              this.__values__[prop] = value;

              let observers = this.__observed__[prop] || [];
              let len = observers.length;

              let values = this.__get_values__();
              for (var i = 0; i < len; i++) {
                let { el, attr, context, bindings } = observers[i];

                for (let i = 0; i < bindings.length; i++) {
                  context = bindings[i](context, values);
                }

                el[attr.replace(":", "")] = context;

                if (!["array", "object"].includes(typeof context)) {
                  if (!!el.setAttribute) {
                    el.setAttribute(attr.replace(":", ""), context);
                  }
                }
              }
            }
          },
        };

        try {
          Object.defineProperties(this, p);
        } catch (e) {
          console.log("Error defining properties", e);
        }
        // } else {
        //   console.log("Already exists?", this[prop]);
        // }
      });

      Object.keys(this.__observed__).map((key) => {
        if (this.hasAttribute(key)) {
          this[key] = this.getAttribute(key);
        }
      });

      this.__render__();

      if (!!this.connected) {
        this.connected();
      }
    }
  }

  __parseTemplate__(template) {
    const eventExp = /@([a-z|A-Z]+)/;
    const attrExp = /:([a-z|A-Z]+)/;
    const boundExp = /{{(.*?)}}/g;

    const getBinding = (el, attr) => {
      let binding = this.__bindings__.find((b) => b.el === el);
      if (!binding) {
        binding = {
          el: el,
          attrs: {},
        };

        this.__bindings__.push(binding);
      }

      if (!binding.attrs[attr]) {
        let context = el[attr];
        if (!!el.getAttribute) {
          context = el.getAttribute(attr);
        }

        binding.attrs[attr] = {
          context: context,
          bindings: [],
        };
      }

      return binding;
    };

    const bindValue = (el, attr, match) => {
      let binding = getBinding(el, attr);

      if (attr === "textContent") {
        let fn = new Function(
          "scope",
          `
          with(scope) {
            return ${match[1]}
          }`,
        );

        binding.attrs[attr].bindings.push((ctx, values) => {
          return ctx.replace(match[0], fn(values));
        });
      }
    };

    const bindAttribute = (el, attr, context) => {
      let binding = getBinding(el, attr);

      let fn = new Function(
        "scope",
        `
        with(scope) {
          return ${context}
        }`,
      );

      binding.attrs[attr].bindings.push((ctx, values) => {
        return fn(values);
      });
    };

    const bindEvent = (el, attr, context) => {
      let fn = new Function(
        "scope",
        `          
        with(scope) {
          return ${context}
        }`,
      );

      el.addEventListener(attr.replace("@", ""), (e) => {
        let values = this.__get_values__();

        values["$event"] = e;

        fn(values);
      });
    };

    const walk = (el) => {
      let attrs = [...(el.attributes || [])];
      let children = [...(el.childNodes || [])];

      if (el.nodeType === 3) {
        let match;
        while ((match = boundExp.exec(el.textContent)) !== null) {
          bindValue(el, "textContent", match, el.textContent);
        }
      } else {
        for (var i = 0; i < attrs.length; i++) {
          if (attrExp.test(attrs[i].name)) {
            bindAttribute(el, attrs[i].name, attrs[i].value);
            // el.setAttribute(attrs[i].name.replace(":", ""), attrs[i].value);
          }

          if (eventExp.test(attrs[i].name)) {
            bindEvent(el, attrs[i].name, attrs[i].value);
            // el.setAttribute(attrs[i].name);
          }
        }
      }

      children.map((c) => {
        walk(c);
      });
    };

    walk(template);
  }

  __get_values__() {
    let values = {};

    let methods = Object.getOwnPropertyNames(
      Object.getPrototypeOf(this),
    ).filter((method) => {
      return typeof this[method] === "function";
    });

    let keys = Object.keys(this.__observed__);
    let callable = [...keys, ...methods];

    for (let i = 0; i < callable.length; i++) {
      if (typeof this[callable[i]] === "function") {
        Object.defineProperty(values, callable[i], {
          get: () => {
            return this[callable[i]].bind(this);
          },
        });
      } else {
        Object.defineProperty(values, callable[i], {
          get: () => {
            return this[callable[i]];
          },
        });
      }
    }

    return values;
  }

  __render__() {
    let values = this.__get_values__();

    for (var i = 0; i < this.__bindings__.length; i++) {
      let binding = this.__bindings__[i];
      let el = binding.el;

      for (let attr in binding.attrs) {
        let { context, bindings } = binding.attrs[attr];

        this.__current_target__ = { el, attr, context, bindings };

        for (let i = 0; i < bindings.length; i++) {
          context = bindings[i](context, values);
        }

        el[attr.replace(":", "")] = context;

        if (!["array", "object"].includes(typeof context)) {
          if (!!el.setAttribute) {
            el.setAttribute(attr.replace(":", ""), context);
          }
        }
      }
    }

    this.__current_target__ = null;
  }
}