AES encryption by JS can’t be decrypted by PHP

Goal

I am doing a proof of concept of a symmetric key encryption. A javascript code will send a AES encrypted string, which i want to decrypt via php.

Attempt

ON the client side, we have:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js" integrity="sha512-a+SUDuwNzXDvz4XrIcXHuCf089/iJAoN4lmrXJg18XnduKK6YlDHNRalv4yd1N40OKI80tFidF+rqTFKGPoWFQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/aes.min.js" integrity="sha512-UOtWWEXoMk1WLeC873Gmrkb2/dZMwvN1ViM9C1mNvNmQSeXpEr8sRzXLmUSha1X4x5V892uFmEjiZzUsYiHYiw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/enc-base64.min.js" integrity="sha512-m/shICarVhgGKyAmv7SlfXVinPq1eNVh4aPdBpGw6lqT3lh/hYtZJ+HaX6DuxjN8o7giM9mfpZQf314bErdGUg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

function encryptFlag(flag, sessionKey) {
    // Encrypt the flag using AES-128-ECB with the session key and no padding
    const hash = CryptoJS.SHA256(sessionKey);
    const encrypted = CryptoJS.AES.encrypt(flag, hash, {
        mode: CryptoJS.mode.ECB,
        padding: CryptoJS.pad.NoPadding    // No padding
    });
    
    console.log("hash", hash);
    // Return the encrypted flag in hex format
    return encrypted.toString();
}

const encryptedFlag = encryptFlag(flag, key);


axios.post('/my-end-point', {
        command: 'login_current_device',
        encrypted_flag: encryptedFlag,
        user: username
    }).then(response => {
        console.log('Server response:', response.data);
        if (response.data.success) {
            window.location.href = "/collab-apex/dashboard";  // Redirect to dashboard if login successful
        } else {
            alert('decoding failed!');
        }
    }).catch(error => {
        console.error('Error during request:', error);
    });

On the server, we have:

$decryptedFlag = decryptFlag($encryptedFlag, hash('sha256',$sessionKey));

function decryptFlag($encryptedFlag, $sessionKey) {
    // Decrypt using AES-128-ECB and the session key
    $decrypted = openssl_decrypt(
        base64_decode($encryptedFlag),
        'AES-128-ECB', // AES encryption mode
        $sessionKey,   // Session key as the secret key
        OPENSSL_RAW_DATA
    );

    return $decrypted;
}

Exact problem

The php is returning an empty string. It has the correct session key there. The encrypted flag is also correct.

Attempt to solution

First, I check with this page.

This is my flag: n/ghZub%h/gh3678 (it can be any alphanumeric + symbols string, no control char).

The javascript, using AES 128, ECB, no padding with key PPEGFtHt9+n6z]1p gives the result:

tjjg2Sfi0D7c5Oxjb4sJ7cmbXrpic+lknjNLI3ojeO0=

But the webpage linked returns: JQE/sqMZCNegPJIric+RUA==, which can be decrypted by the same site. (un)surprisingly, the Javascript output can’t be decoded to the correct result.

If I specifically set return encrypted.toString(CryptoJS.enc.Base64); I get error :

Error during request: TypeError: r.clamp is not a function
    stringify https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/enc-base64.min.js:1
    toString https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js:1

If i do not include the base64 js file from cdnjs, then the error is:

Error during request: TypeError: wordArray.clamp is not a function
    stringify https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.js:1311
    toString https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.js:3998

if i set return encrypted.toString(CryptoJS.enc.Hex);, the result in javascript is an empty string.

Back at php side, it is an empty string in any case; even if i use :

$decrypted = openssl_decrypt(
        $encryptedFlag, // i removed the base64decode here
        'AES-128-ECB', // AES encryption mode
        $sessionKey,   // Session key as the secret key
        OPENSSL_RAW_DATA
    );

I looked at this SO question – and that is how I came to the idea of hash. Even without hashing the session key, php is always returning empty string.

Session key is 16 chars long, which is 128 bit.

Question

How can I resolve the situation, that a symmetric key cryptographic exchange works between JS and PHP?

I would like to find what is wrong in my implementation; but I am open to a different symmetric key algorithm if you tell me the full code for implementation. The requirements are that

  1. it has to be symmetric key, and
  2. it has to work between JS and PHP.

Thank you.

swig: Failed to Download Resource “swig” Due to Connection Timeout

I am encountering an issue while trying to download and install Watchman using the Homebrew command brew install watchman. The process fails during the download with the following error message:

==> Fetching swig
==> Downloading https://raw.githubusercontent.com/Homebrew/homebrew-core/0f6a079
######################################################################### 100.0%
==> Downloading https://downloads.sourceforge.net/project/swig/swig/swig-4.3.0/s
######################################################################### 100.0%  
curl: (28) Failed to connect to cyfuture.dl.sourceforge.net port 443 after 75084 ms: 
Couldn't connect to server
Warning: Problem : timeout. Will retry in 1 seconds. 3 retries left.
######################################################################### 100.0%
Failed to connect to 
cyfuture.dl.sourceforge.net port 443 after 75003 ms: Couldn't connect to server
Warning: Problem : timeout. Will retry in 2 seconds. 2 retries left.
######################################################################### 100.0%  - 
Failed to connect to 
cyfuture.dl.sourceforge.net port 443 after 75002 ms: Couldn't connect to server
Warning: Problem : timeout. Will retry in 4 seconds. 1 retries left.
######################################################################### 100.0%                                   
curl: (28) Failed to connect to cyfuture.dl.sourceforge.net port 443 after 75064 ms: 
Couldn't connect to server

The issue seems to be a connection timeout when attempting to download the resource from the SourceForge server.

Solutions I’ve Tried:

  1. Disabled the firewall.
  2. Disabled the proxy.
  3. Tested the connection with and without VPN.
  4. Uninstalled and reinstalled SWIG and Homebrew.
  5. Restarted the system.

Additionally, I tried the following commands:

  1. brew cleanup -s

  2. brew install –force watchman

  3. watchman shutdown-server

  4. brew update

  5. brew reinstall watchman

  6. rm -rf /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core; brew update

Despite these efforts, the issue persists.

Has anyone experienced a similar issue or have any suggestions on how to resolve it?

Editing PDF in Web

I am currently generating PDFs with react-pdf. Now the user should be able to edit some texts. Is there an ability out there for the following example:

  1. I generate a PDF.
  2. The User wont download it > instead it is shown in a web based editor.
  3. The user can edit text and download the own edited PDF.

please tell what type i use in file .tsx or js in next js?

I’m working on a Next.js project and trying to figure out the best way to handle types in either .tsx or .js files. I’m a bit confused about when and how to use TypeScript types correctly within a Next.js setup.
Project Setup
My project is set up with TypeScript, so I’m using .tsx files. However, I sometimes come across examples that use .js files in Next.js, and I’m not sure if the typing approach should be different in those cases.

Problem Statement
I’d like to understand how to properly define types for components in Next.js. Specifically, I’m unsure about the correct approach for defining types for props or states.

getComputedStyle/getPropertyValue returns wrong results in extension

I’m building a Safari extension. I’m dealing with a DOM element whose -webkit-user-select style property is set to ‘none’. I can see that in the browser developer tools.

However, when I programmatically access the same element from within the context of my extension, the same property evaluates to an empty string, as if it weren’t set. This happens even after I check the value manually in the developer tools.

window.getComputedStyle(el).getPropertyValue('-webkit-user-select');
// => ''

For context, I’m iterating over a bunch of elements in the user’s selection to decide how to process them. Maybe that has something to do with it but I don’t know.

function anyIllegalElements(selection) {
  return !!selectedElements(selection).filter(el => {
    return window.getComputedStyle(el).getPropertyValue('-webkit-user-select') === 'none' ||
      window.getComputedStyle(el).getPropertyValue('user-select') === 'none' ||
      el.tagName === 'IFRAME';
  }).length;
}

What’s going on here? I know that this behavior would be expected if I tried accessing the properties directly, but I’m using getPropertyValue as advised.

How to remove the last entry from browser history stack without allowing forward navigation?

I have a React application where I’m implementing navigation between different pages. Here is the flow I need help with:

  • The user starts on a main page, let’s call it Page A.
  • From Page A, the user navigates to a subpage Page B (which will create page with unique id and treated as edit in te background).
  • On Page B, on click of a button, it should:
    – Before navigating the data created with unique id will be deleted.
    – Navigate the user back to Page A.
    – Remove the history entry of Page B completely so that if the user tries to go back or forward in the browser (to the already deleted page), they shouldn’t be able to, because they will see a error in this case.

I’ve tried using navigate(-1) to go back one step, but this still keeps Page B in the forward navigation stack. I also tried using navigate(‘..’, { replace: true }), but this seems to create duplicate entries in the history stack for Page A, which confuses users.

Expected solution:

I want to effectively “pop” the last history entry (Page B) so that once the user navigates back to Page A, they cannot use forward navigation to return to Page B.
The browser history should look like the user was never on Page B once they navigate back to Page A.
Is there a way to manipulate the browser history stack or use a different approach to achieve this behaviour?

Any insights or examples would be greatly appreciated!

EMFILE too many open file on MAC

I’m getting the error on my MAC

“EMFILE too many open file”. I had send my porject zip from ubuntu to MAC could this be the reason?

I did ulimit -n command, installed watchment alrerady. Have already gone throught many youtube solutions but it doesn’t work.

Error

Remove strange characters from Html

enter image description here

There are two places in the image where I can’t figure out how they appeared, and I’ve searched all the files globally and can’t find the javascript code that inserted those characters.

Is there any tool that can detect who inserted these characters? Or how to remove these characters from the body.

trying to get blob image from a url using selenium and java

I’m trying to download an image blob from a web page. Blob url looks like : blob:https://example.com/43bdcaf9-7d30-4b80-9306-7e411533b960

The image can be downloaded and converted to base64 text if I use javascript like below (in Chrome browser and running code as a browser extension):

async function blobToBase64(blobUrl) {
    console.log(blobUrl);
    const response = await fetch(blobUrl);
    const blob = await response.blob();
    const arrayBuffer = await blob.arrayBuffer();
    const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));
    return base64String;
}

I need the same thing to be done in Java using Selenium Chrome driver, but not able to achieve it. I have tried below code:

                    String blob = "";
                    if (driver instanceof JavascriptExecutor) {
                        blob = ((JavascriptExecutor) driver).executeScript("const response = await fetch('" + baseUrl
                                + "');" + "const blob = await response.blob();"
                                + "const arrayBuffer = await blob.arrayBuffer();"
                                + "const base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));"
                                + "return base64String;").toString();

                        System.out.println(blob);
                    }

but I get failed to fetch url error.
Please help to fetch the image blob and convert to base64 String

org.openqa.selenium.JavascriptException: javascript error: Failed to fetch
  (Session info: chrome=130.0.6723.91)
Build info: version: '4.18.1', revision: 'b1d3319b48'
System info: os.name: 'Windows 11', os.arch: 'amd64', os.version: '10.0', java.version: '23'
Driver info: org.openqa.selenium.remote.RemoteWebDriver

Thanks in advance.

Copy to cliboard msg is not visible

  • here is the html(tailwind included) and js code-
<!-- Output Display with Animated Border -->
                        <div class="relative mb-6 flex items-center overflow-hidden rounded-lg p-[1.8px] border border-gray-300">
                          <!-- Animated Border Layer -->
                          <div class="animate-rotate absolute inset-0 h-full w-full rounded-lg bg-[conic-gradient(#0e85df_20deg,transparent_120deg)]"></div>

                          <!-- Inner Password Output Container -->
                          <div class="relative z-20 w-full">
                            <input type="text" id="password-output" readonly placeholder="Your Password" class="w-full py-3 px-4 bg-gray-100 rounded-lg text-blue-600  focus:outline-none focus:ring-red-600 font-semibold font-sans text-xl" />
                            
                            <!-- Copy Button -->
                            <button id="copy-btn" aria-label="Copy Password" class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-blue-500 focus:outline-none">
                              <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2z" />
                              </svg>
                            </button>

                            <!-- Copy Message -->
                            <span id="copy-msg" class="absolute left-2 -bottom-6 text-sm text-green-500 hidden opacity-0 transition-opacity duration-500">Copied!</span>
                          </div>
                        </div>
                    

and js-

async function copyContent() {
    //this is an api used to copy something on clipboard, it will return promise
    //jo bhi outputtab pr show hoga vo sab dikhega
    

    try {
        await navigator.clipboard.writeText(outputTab.value);
        copymsg.classList.remove('hidden');
        copymsg.classList.add('opacity-100');
        copymsg.innerText='Copied!!'
        console.log("copy succesfull")
        
    } catch (error) {
        copymsg.innerText='failed to copy!'
        
    }
    // to make copy span visible
    //this makes any css made in copybtn visible for sometimes
    
    

    //to make copt span hide
    setTimeout(()=>{
        copymsg.classList.add('hidden');
        copymsg.classList.add('hidden');
    },2000);

}

i have invokd the function after this

im expecting to see the copied msg bt it is not visible , although my code is working bt only msg is not visible

Do I need to use await when calling connection.release() in mysql2?

I’m working with the mysql2 Node.js library and managing connections through a connection pool. In some of the examples I’ve seen, connection.release() is called with await, but based on the documentation, release() seems to be a synchronous method that doesn’t return a promise.


let connection;
try {
    connection = await db.getConnection(); // Using await to get the connection
    await connection.beginTransaction();   // Using await to begin the transaction
    
    // Some other database operations go here
    
} catch(error){

}finally {
    if (connection) {
        await connection.release(); // Do I need to await here?
    }
}


Why aren’t UUIDs generated in Node.js saving correctly to PostgreSQL as UUID data types?

I’m working on a Node.js application with Express, PostgreSQL, and Passport.js to allow user registrations. In this setup, I generate UUIDs on the server side using uuidv4 for each user’s unique identifier (id field). The id column in my PostgreSQL users table is set to the UUID data type to match.

Although the database connection is successful, the registration process doesn’t seem to store data in the database. I see no rows affected in PostgreSQL’s query logs, even though I see console logs in Node.js that appear to indicate successful generation of UUIDs and user data. Also, in some error messages, the UUIDs seem to be interpreted as integers rather than UUID strings.

Context and setup details:

Database schema: id is defined as UUID in the users table.
Environment: Node.js with Express, PostgreSQL, and pg for the database connection.
Library used for UUID generation: uuidv4 from the uuid library.
Steps I’ve already taken to troubleshoot:

Verified that UUIDs are generated correctly and logged them to the console.
Confirmed that the database connection is successful, with no connection errors.
Checked the users table schema to ensure id is set as UUID.
Attempted specifying ::uuid casting in the SQL query to make sure the UUIDs are recognized as such in PostgreSQL.
Tried removing RETURNING * from the query to simplify it.
Despite these efforts, the users table remains empty, and the query logs show “0 rows affected.”

const { v4: uuidv4 } = require('uuid');
const bcrypt = require("bcrypt");
const { Pool } = require('pg');

const pool = new Pool({
    user: process.env.DB_USER,
    host: process.env.DB_HOST,
    database: process.env.DB_DATABASE,
    password: process.env.DB_PASSWORD,
    port: process.env.DB_PORT || 5432
});

app.post("/register", async (req, res) => {
    try {
        const hashedPassword = await bcrypt.hash(req.body.password, 10);
        const newUser = {
            id: uuidv4(),  // Generating UUID
            name: req.body.name,
            email: req.body.email,
            password: hashedPassword
        };
        
        const insertQuery = `
            INSERT INTO users (id, name, email, password) 
            VALUES ($1::uuid, $2, $3, $4) RETURNING *
        `;
        const values = [newUser.id, newUser.name, newUser.email, newUser.password];

        pool.query(insertQuery, values, (err, result) => {
            if (err) {
                console.error("Error executing query", err);
                return res.status(500).send("Server error");
            }
            console.log("User inserted:", result.rows[0]);
            res.redirect("/login");
        });
    } catch (e) {
        console.error(e);
        res.redirect("/register");
    }
});

Database Schema

CREATE TABLE users (
id UUID PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL

);

  1. Why might PostgreSQL not be storing data in the users table, even though the Node.js server logs seem to indicate successful user registration?
  2. .Could there be any specific configurations in PostgreSQL or in the pg library that would explain why UUIDs are misinterpreted as integers?
  3. Are there additional steps to ensure that UUIDs generated in Node.js are correctly recognized and stored in PostgreSQL?

Any insights or advice would be greatly appreciated!

Expo File System not properly reading file’s binary

I’m trying to implement Azure’s Speech-To-Text SDK in a Expo managed React-Native app.
I’m using Expo-av to record a .wav file, Expo-file-system to read the file as binary and a PushAudioStream to append the binary to.

  • Here are some of the results:
    LOG Recognized: Comma The comma said that he was not commuted to his owner’s commissioner’s house. Comma and he said he said that he said he was a commissioner and he said he was a commissioner of the state of Commissioner’s House. Period Comma 2018.
    LOG Recognizing: comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma
    LOG Recognizing: comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma comma

  • As you can see, there are a lot of commas.

  • IF I save the file to my machine and send it to azure through Postman I get the correct transcription.

  • My guess is that the binary isn’t properly read.

Please help, I’ve been on this problem for 5 days now. I no longer know what to do.

This is the code:

import {useEffect, useRef, useState} from "react";
import {Audio} from "expo-av";
import * as FileSystem from 'expo-file-system';
import {EncodingType} from 'expo-file-system';
import * as Sharing from 'expo-sharing'
import {
  AudioConfig,
  PushAudioInputStream,
  ResultReason,
  SpeechConfig,
  SpeechRecognizer
} from "microsoft-cognitiveservices-speech-sdk";
import {
  AndroidAudioEncoder,
  AndroidOutputFormat,
  IOSAudioQuality,
  IOSOutputFormat
} from "expo-av/build/Audio/RecordingConstants";
import {Platform} from "react-native";

const RecordingOptions: Audio.RecordingOptions = {
  isMeteringEnabled: true,
  android: {
    extension: '.wav',
    outputFormat: AndroidOutputFormat.MPEG_4,
    audioEncoder: AndroidAudioEncoder.AAC,
    sampleRate: 16000,
    numberOfChannels: 1,
    bitRate: 16000,
  },
  ios: {
    extension: '.wav',
    outputFormat: IOSOutputFormat.LINEARPCM,
    audioQuality: IOSAudioQuality.HIGH,
    sampleRate: 16000,
    numberOfChannels: 1,
    bitRate: 128000,
    linearPCMBitDepth: 16,
    linearPCMIsBigEndian: false,
    linearPCMIsFloat: false,
  },
  web: {
    mimeType: 'audio/wav',
    bitsPerSecond: 128000,
  },
}

export const useAzureSpeechStream = (key: string, region: string, language: "en-US" | "ro-RO") => {
  const [recording, setRecording] = useState<Audio.Recording | null>(null);
  const [isRecording, setIsRecording] = useState<boolean>(false);
  const [transcript, setTranscript] = useState<string>("");
  
  const intervalRef = useRef<NodeJS.Timeout | null>(null);
  const lastProcessedPosition = useRef(0);
  const stream = useRef(PushAudioInputStream.create());
  
  const speechConfig = SpeechConfig.fromSubscription(key, region)
  
  speechConfig.speechRecognitionLanguage = language;
  const audioConfig = AudioConfig.fromStreamInput(stream.current);
  const recognizer = useRef(new SpeechRecognizer(speechConfig, audioConfig));
  
  useEffect(() => {
    recognizer.current.recognizing = (s, e) => {
      if (e.result.reason === ResultReason.RecognizingSpeech) {
        console.log(`Recognizing: ${e.result.text}`);
        setTranscript((prev) => prev + " " + e.result.text);
      }
    };
    
    recognizer.current.recognized = (s, e) => {
      if (e.result.reason === ResultReason.RecognizedSpeech) {
        console.log(`Recognized: ${e.result.text}`);
        setTranscript((prev) => prev + " " + e.result.text);
      }
    };
    
    recognizer.current.startContinuousRecognitionAsync();
    
    return () => {
      recognizer.current.stopContinuousRecognitionAsync();
      recognizer.current.close();
    };
  }, []);
  
  const requestAudioPermissions = async () => {
    const response = await Audio.requestPermissionsAsync();
    return response.status === 'granted';
  };
  
  const startRecording = async () => {
    try {
      if (recording) {
        await recording.stopAndUnloadAsync();
        setRecording(null);
      }
      
      const hasPermission = await requestAudioPermissions();
      if (!hasPermission) {
        throw new Error('Permission to access microphone is required!');
      }
      
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: true,
        playsInSilentModeIOS: true,
        staysActiveInBackground: true,
        shouldDuckAndroid: true,
      });
      
      const newRecording = new Audio.Recording();
      await newRecording.prepareToRecordAsync(RecordingOptions);
      await newRecording.startAsync();
      setRecording(newRecording);
      setIsRecording(true);
      console.log('Recording started');
      
      intervalRef.current = setInterval(async () => {
        const uri = newRecording.getURI();
        if (uri) {
          const fileInfo = await FileSystem.getInfoAsync(uri, {size: true})
          if (fileInfo.exists) {
            const fileString = await FileSystem.readAsStringAsync(uri, {
              encoding: EncodingType.Base64,
            })
            
            const fileBuffer = Buffer.from(fileString, 'base64')
            
            stream.current.write(fileBuffer)
          }
        }
      }, 500);
      
    } catch (error) {
      console.error('Failed to start recording:', error);
      setIsRecording(false);
      throw error;
    }
  };
  
  const stopRecording = async () => {
    if (!recording) return;
    
    try {
      await recording.stopAndUnloadAsync();
      const shareUri = recording.getURI()
      setRecording(null);
      setIsRecording(false);
      
      if (intervalRef.current) {
        clearInterval(intervalRef.current as NodeJS.Timeout);
        intervalRef.current = null;
      }
      
      stream.current.close();
      recognizer.current.stopContinuousRecognitionAsync();
      // await shareCacheFile(shareUri)
    } catch (error) {
      console.error("Error stopping recording:", error);
      throw error;
    }
  };
  
  return {
    isRecording,
    transcript,
    startRecording,
    stopRecording,
  };
};

I will buy a beer for everyone who’s suggestion gets me in the right direction. Cheers.

  • Sending it through postman seems to work

How to test a button click in Jest

I’m writing a website that has an interactive deck of cards. What I want to check is that when I click the deck-shuffle-btn the function shuffleDeck is run.

I’m having a really hard time with this, and I can’t seem to come up with anything that works as expected.

So I’ve got my event listener in my script.js file :

document.addEventListener("DOMContentLoaded", () => {
    document.getElementById("deck-shuffle-btn").addEventListener("click", deckShuffle(deck.shuffledDeck));
    });

I also have an object named deck that has the deck of cards in it, a function called deckShuffle() that re-arranges the order of the cards and a function called resetDeck() that returns everything back to default values before shuffling the cards once more.

So when this button is clicked, it should run deckShuffle() and shuffle the deck.

Then I’ve got my test in my script.test.js file:

describe("Test deckShuffle function when using the deck-shuffle-btn element", () => {
    beforeAll(() => {
        // sets a mock HTML site with a single button element in it
        document.body.innerHTML =
            '<div>' + +
            '<button id="deck-shuffle-btn"></button>' +
            '</div>';
        // reset the deck object
        resetDeck();
        // takes a copy of the deck to run tests against
        testElement = [...deck.shuffledDeck];
        // trigger a button click
        $("deck-shuffle-btn").trigger("click");
    });
    test("shuffledDeck is not the same order as testElement, showing that the deck has been shuffled", () => {
        expect(deck.shuffledDeck).not.toEqual(testElement);
    });
})

This test fails, showing that the deck has not been shuffled when the button is clicked as I expected.

I suspect this is something to do with how I am implementing my test, rather than how I am implementing the event listener in my code.

I am super new to this, and I am really struggling with Jest testing. Any help and advice would be appreciated!!