How to get variable value in a script with jest puppeteer? - javascript

I'm trying to make unit and e2e test on a project, i decided to use jest and puppeteer (with also jest-puppeteer) to achive this.
My problem is that I initialize a var, named tools, in an script of index.html and i want to get it to do some test after, but he return me an error that is "tools" is not defined.
I already tryed to see on the web if a solution exist but without success.
Can I have somme help ? :')
Code extracts:
// index.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<script src="./js/Variables.js"></script>
<script src="./js/Tools.js"></script>
</head>
<body>
<script>
tools = new Tools();
</script>
</body>
</html>
// Variables.js
let tools;
// Tools.js
class Tools {
constructor(){
// do some stuff
}
test(){
return "test string";
}
}
// app.test.js
beforeAll(async () => {
await page.goto('http://myPage/');
});
test("can i get \"tools\"", () => {
console.log(tools); // tools is not defined
expect(tools.test()).toBe("test string");
});
EDIT 22/07/2022 15:38
I finally managed to get something BUT now i can't use functions on it, the error says that tools.test() is not a function, it seems to retrieve only his "pure" value and not the Tools instance.
test("can i get \"tools\"", async () => {
let tools = await page.evaluate('tools');
console.log(tools); // gets {} (doesn't seems to retrieve instance)
expect(tools.test()).toBe("test string"); // TypeError: tools.test() is not a function
});

I can use class method by using
let toolsTest = await page.evaluate(() => tools.test());
BUT it's not really what i want... I really want to get an instance to test some sh!t on it.
However let tools = await page.evaluate(() => tools); still doesn't give me the instance, there is really no way to achive this ?
So i really need to know how to get variables in script tag to use them with jest. Maybe another test library can do the job such as mocha ?

Related

How can I get the access of a const inside my function?

How can I grant access to my const to be used inside a function? In this case I want to access my const catName inside my function fetchListings. I'm getting this error:
Question Updated:
ReferenceError: catName is not defined
<script context="module">
const fetchListings = async () => {
try {
// get reference to listings collection
const listingsRef = collection(db, 'listings');
// create a query to get all listings
const q = query(
listingsRef,
where('type', '==', catName),
orderBy(timestamp, 'desc'),
limit(10)
);
// execute query
const querySnap = await getDocs(q);
let lists = [];
querySnap.forEach((doc) => {
console.log(doc);
});
} catch (error) {
console.log(error);
}
};
fetchListings();
</script>
<script>
import { page } from '$app/stores';
// export the const catName to the function above
export const catName = $page.params.catName;
</script>
Hi {catName}!
The problem you are running into is coming from how the <script context="module"> works.
The module level script tag serves as a one-time-per-app setup script. That means it will run only one time, when your app is initialized and it will be run before any of the regular <script> tag code is run. See: https://svelte.dev/docs#component-format-script-context-module
This mean that the <script context="module"> won't have any access to what's defined or created in the normal <script> tags' code. Thus the not defined error for your constant, which is defined in the regular <script> tag.
Based on this, your code would need to be refactored (reorganized). My understanding is that you put the fetchListings in the module context because you want to pre-fetch the results and only do it once during the startup of your app.
To accomplish that you can refactor your code like this:
<script context="module">
let preFetched=false
</script>
<script>
import { page } from '$app/stores';
// export is not needed
const catName = $page.params.catName;
async function fetchListings() => {
// Your code ...
}
if(!preFetched){
fetchListings()
preFetched=true
}
</script>
Hi {catName }!
This ensures that the fetchListings function only runs once. The trick is that variables, constants, etc defined in the module context are accessible to all instances of that model. So when the first instance is being created it will run the fetchListings function, and sets the preFetched variable to false, so the subsequent instances will no do that.
This is just one possible way to do it. Depending on what exactly you want to accomplish you might want to organize things differently. But with the understanding of what does the <script context="module"> do and when it runs, you should be able to come up with your own solution best suited for your needs.

Display cpu info in electron

So I started learning electron and I made a cool app. I wanted to add more things, but I'm kinda stuck. I tried to add cpu name using manufacturer(); in systeminformation. I have no idea what have I done wrong. I'll remind you last time that I'm a complete beginner, so expect me to be kinda dumb.
This is how my div I want to make looks like
<div class="cpu_name">
CPU name: <span id="cpu-name">-</span>
</div>
<div class="cpu_cores">
CPU cores: <span id="cpu-cores">-</span>
</div>
<div class="cpu_temperature">
CPU temperature: <span id="cpu-temperature">-</span>
</div>
</div>
Also, I didn't forget to use script
<script src="./renderer.js" defer type="module"></script>
<script src="./js/window.js" defer type="module"></script>
Next thing I've done was adding it into the renderer.js
I created var linked to the #cpu-name
const CPU_NAME = document.getElementById("cpu-name");
Then I created getCpuName() function
async function getCpuName(){
const name = await app.cpuName();
const cpu_name = name.manufacturer;
updateCpuName(cpu_name);
}
Since I called a updateCpuName() method, I created one
function updateCpuName(cpu__name){
CPU_NAME.innerText = cpu__name;
console.log(cpu__name);
}
Next thing I've done was adding it into the preload.js. I've done the preload.js by tutorial, because I don't really uderstand everything there yet...
This is my whole preload.js script
const os = require("os");
const { ipcRenderer, contextBridge } = require("electron");
const API = {
window:{
close: () => ipcRenderer.send("app/close"),
minimize: () => ipcRenderer.send("app/minimize"),
},
cpuUsage: (data) => ipcRenderer.invoke("cpu/get", data),
cpuName: (data) => ipcRenderer.invoke("cpu/name", data),
}
contextBridge.exposeInMainWorld("app", API);
But this cpuName: (data) => ipcRenderer.invoke("cpu/name", data), is the only important thing here.
Last thing I did was adding it into the index.js.
So I basically created const with systeminformation
const {currentLoad, manufacturer, cpu } = require("systeminformation");
and then made a ipcMain.handle();
ipcMain.handle("cpu/name", async (_, data) => {
const name = await manufacturer();
return name;
});
I know for sure I've done something wrong, but I'm not able to figure out what. It's just fun project I'm working on to get better in it. Thank you for even just reading this ^-^
There is an npm module called systeminformation (https://www.npmjs.com/package/systeminformation) that will help you with this. I have never accessed the CPU using electron or this module, but the module seems very easy to use and is very well documented.
Please also look at the "Known Issues" section of the module readme. Some additional dependencies are required for checking CPU temperature, so you might want to look at that too!

Calling JavaScript methods from typescript

Currently, I'm trying to use work with the online view of the document and I came across this website.https://rollmyfile.com/.
when I head towards the developer APIs https://rollapp.readme.io/v1.0/docs/rollmyfile-js-api. I find I can read any document by passing the URL. But I'm working on angular 4 which uses typescript so I did call out
so I placed the js code in my index.html
index.html
<script type="text/javascript" src="https://api.rollapp.com/1/js/rollmyfile.js"></script>
now I need to make use of this js file to pass the Url to it something this.
<script type="text/javascript">
var key = "SeCur3AP1K3y";
var rollMyFile = new RollMyFile(key);
</script>
rollMyFile.openFileByUrl("https://www.example.com/documentation/overview.docx");
so, I need to make use of .openFileByUrl in my .component.ts
I have referred the path as well in my .component.ts something like this
///<reference path="https://api.rollapp.com/1/js/rollmyfile.js"/>
but still, I'm not able to create an instance of RollMyFile.
I'm getting [ts] Cannot find name 'RollMyFile' error.
Is there something that I'm doing wrong?
You need to declare RollMyFile on top in order to use that function in your TS file
declare const RollMyFile: any;
ngOnInit() {
console.log("hi");
var key = "SeCur3AP1K3y";
var rollMyFile = new RollMyFile(key);
console.log('rollMyFile',rollMyFile)
}
This console giving me error because of having wrong secret key.
try this in your component :
declare RollMyFile: any;
constructor(private _script: ScriptLoaderService) {}
ngOnInit() {
this._script.load('body', 'https://api.rollapp.com/1/js/rollmyfile.js')
.then(result => {
});
}
}
and use RollMyFile in your code something like this:
(<any>RollMyFile).somfunction();

document.getElementById not working with Webpack

I've been trying to implement basic Javascript with Webpack but have found a lot of trouble trying to get basic methods to work. I know it is probably a problem with my js loading before the DOM but all my fixes to this issue have been futile.
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
I've tried quite a few different solutions to make this code operate after the DOM has loaded but nothing seems to work.
I tried the basic attempt of just putting my javascript file at the bottom of the DOM.
I've tried a basic js method like so:
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
function initApplication() {(... placed code in here ...)}
I've tried using jqueries
$( document ).ready(function() { });
I've tried injecting my javascript file into my HTML with the html-webpack-plugin
Is there something I'm missing with Webpack?
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample Site</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<button id="new-task">click</button>
<script src="/assets/app.bundle.js"></script>
</body>
</html>
app.js
'use strict';
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
}
function initApplication() {
onButtonClick();
}
let onButtonClick = function() {
let taskInput = document.getElementById('new-task');
taskInput.onclick = alert('hey');
}
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
First of all, module code in webpack bundles is run in its own scope, effectively in a closure, and not in the global scope (which you can access with window). Secondly, even if you go to your console and declare a variable with let, for example let a = 1, even though your console operates in the global (window) scope, if you try to do window.a you will get undefined because let variables do not get attached to the window scope. See this
Is there something I'm missing with Webpack?
Probably the fact that the code in your bundles, generated by webpack, does not run in the global scope, as explained above.
If you want to expose a variable to the global scope in order to be able to use it, for debugging purposes, in the console, declare it as window.variableName. Or you can add a breakpoint in your code, by adding debugger, after the variable you want to check out, without exposing it to the global scope.
Using the browser console you can only access variables which are declared globally, and if you define a JavaScript variable inside a function, it doesn't become a global variable. Also, variables declared using let never become global variables.
If you want to declare a variable globally, you can make it a property of window, for example:
window.taskinput = document.getElementById('new-task');
This might be useful for debugging, but avoid doing it in production code, as using global variables is considered a bad practice.

How can I stub/mock a function in the javascript global namespace

I'm trying to stub/mock/override a function call during testing which writes a log to a DB.
function logit(msg) {
writeMessageToDb(msg);
}
function tryingToTestThisFunction(){
var error = processSomething();
if (error) {
logit(error);
}
}
I'd like logit() to simply print to the console during testing...and doing a "isTesting()" if/else block inside the logit() function is not an option.
Is this possible without including some additional mocking framework. I'm currently using JsTestDriver for unit testing and have not had a chance to evaluate any mocking frameworks. An ideal solution at the moment would be to handle this without another framework.
I use Jasmine and Sinon.js (using Coffeescript), here's how I stub out the confirm() method to, for example, just return true.
beforeEach ->
#confirmStub = sinon.stub(window, 'confirm')
#confirmStub.returns(true)
afterEach ->
#confirmStub.restore()
In javascript the latest definition is the prevalent.
so just redefine the logit method after the first definition.
function logit(msg) {
console.log(msg);
}
example : http://www.jsfiddle.net/gaby/UeeQZ/
I have been working on exactly the same problem. The developers gave me an HTML5 app to test, so of course I can't change their code for testing. I decided to use qunit and sinon, along with sinon-qunit.
For a newb to JavaScript unit testing like me, I was going nuts with the sinon documentation and various examples on the web, as most of it seems for an implied environment that isn't mentioned. The code below is a complete page, so I hope nothing is left for confusion.
The function that I have to call is caller() and I can't do anything about stubme() because it's in the developer's code. However, I can add sinonstub() in my test code. But how to get it to work with sinon? The sinon documentation really confused me for a while, but below is the simple solution. The stub4stubme object can be used to control the stub action, and also get the information about what's happening with the stub calls.
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="qunit-1.12.0.css" type="text/css" media="screen" />
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="sinon-1.7.3.js"></script>
<script src="qunit-1.12.0.js"></script>
<script src="sinon-qunit-0.8.0.js"></script>
<script>
// Dev code in another file
function stubme() {
return "stubme";
}
function caller() {
return "caller " + stubme();
}
// End of dev code
var sinonstub = function () {
return "u haz bin stubbed";
};
test("Stubbing global environments", function () {
equal(caller(), "caller stubme");
var stub4stubme = this.stub(window, "stubme", sinonstub);
equal(caller(), "caller u haz bin stubbed");
ok(stubme.called);
});
</script>
</body>
</html>
Javascript is not only runtime linked, but is the last word wins linked as well. This means you can redeclare the method with the behavior you want in your test (since your test has the last word):
function yourTest(){
oldImpl = logit; // An even better approach is to do this in a setup.
logit = function(msg){ Console.log.apply(console, s.call(arguments));};
// do you assertions: assert.... yada.
logit = oldImpl; // Do this to keep your test isolated from the others you'll be executing in this test run. An even better approach is to do this in a teardown.
}
can you just override the method on the window object? In Chrome console this works
function test() {console.log('test')};
window.test();
just override the logit function, this can be called anytime later than logit is defined.
(function(){
//keep handle to original logit method.
var ol = logit;
//shorter lookup path for slice
var s = Array.prototype.slice;
//logit override
logit = function() {
//if in testing
if (typeof IsTesting == "function" && !!IsTesting()) {
//log the arguments
console.log.apply(console, s.call(arguments));
} else {
//otherwise, call the original function.
ol.apply(this, s.call(arguments))
}
}
}());

Categories