how to get a Network Panel show up when debugging Electron/Node applications - node.js

I am building an electron application and I am using libraries (request/axios) for making requests. One thing I didn't expect is that making these requests on Node won't display a Network Panel when running in chrome debug mode. Is there a simple way to tell debug mode to turn on a network panel for tuning into https requests(I assume these libraries all use https)?
currently on my server side electron app i only see the following

Solution 1 - create your own
you can wrap your axios functions and send an event to your renderer process
main electron process
const electron = require('electron');
const {
app,
BrowserWindow,
ipcMain
} = electron;
const _axios = require('request-promise');
const axios = {
get: (url, params) => _axios.get(url, params).then(sendData),
post: (url, params) => _axios.post(url, params).then(sendData),
delete: (url, params) => _axios.delete(url, params).then(sendData),
put: (url, params) => _axios.put(url, params).then(sendData)
// ...
};
function sendData() {
return (data) => {
mainWindow.webContents.send('network', data);
return data;
};
}
renderer process (index.html):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World!</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css"
rel="stylesheet">
<style>
.kb-debug-widget {
position: fixed;
bottom: 0;
height: 200px;
overflow-x: hidden;
overflow-y: auto;
background: grey;
left: 0;
right: 0;
font-size: 10px;
}
</style>
</head>
<body>
<div class="kb-debug-widget">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth"
id="network">
<tr>
<th>Name</th>
<th>Method</th>
<th>Status</th>
<th>Type</th>
<th>Body</th>
</tr>
</table>
</div>
<script>
require('./renderer.js');
var {
ipcRenderer,
remote
} = require('electron');
ipcRenderer.on('network', (event, response) => {
const networkElement = document.getElementById('network');
// print whatever you want here!
networkElement.innerHTML +=
`
<tr>
<td>${response.request.href}</td>
<td>${response.request.method}</td>
<td>${response.statusCode}</td>
<td>${response.headers['content-type']}</td>
<td>${response. data}</td>
</tr>
`;
// you can also print the network requests to the console with a decent UI by using console.table:
console.table({
name: response.request.href,
method: response.request.method,
status: response.statusCode,
type: response.headers['content-type'],
body: response. data,
});
});
</script>
</body>
</html>
This will create a widget on the bottom of your view.
it's even easier with request:
const _request = require('request-promise');
const _axios = require('request-promise');
// this should cover all sub-methods
const request = (params, callback) => {
return _request(params, callback)
.on('response', (response) => {
mainWindow.webContents.send('network', response);
return response;
});
};
Since both axios & request return similar objects, you can use the same function on the renderer side.
code in action
Here's a GitHub repository with the code implemented
Solution 1: Alt - write network requests to renderer's console
I also added an option to print the requests to the dev tools console, with console.table. Here's how it looks:
You can leave only this method if you don't want a widget inside your HTML.
Solution 2 - Run electron with the --inspect flag
You can also just run electron with the inspect flag, which allows you to debug your server code and has its own network tab with the "server-side" HTTP requests.
In order to see it, run your electron application like so:
electron --inspect=<port> your/app
if you want to immediatly break on the first line, run the same command but replace --inspect with --inspect-brk.
After running the command, open any web-browser and go to chrome://inspect and selecting to inspect the launched Electron app present there.

Expanding on #Thatkookooguy 's answer:
Run electron with the --inspect and --remote-debugging-port flags
You can also just run electron with the inspect flag, which allows you to debug your server code and has its own network tab with the "server-side" HTTP requests.
In order to see it, run your electron application like so:
electron --inspect=<portA> --remote-debugging-port=<portB> your/app
or
/Applications/My.app/Contents/MacOS/Foo --inspect=<portA> --remote-debugging-port=<portB>
if you want to immediatly break on the first line, run the same command but replace --inspect with --inspect-brk.
After running the command, open any web-browser and go to chrome://inspect and selecting to inspect the launched Electron app present there.
hope this helped. If you have any questions, you can ask me in the comments
P.S if you want even more details you can run it like
/Applications/Mattermost.app/Contents/MacOS/Mattermost --no-sandbox --enable-logging --inspect --remote-debugging-port=9222

Related

How to solve useEffect running twice? [duplicate]

Consider the snippets below. With React 18, count gets printed twice to the console on every render but with React 17 it gets printed only once.
React 18 Example:
function App() {
const [count, setCount] = React.useState(0);
console.log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
React 17 Example
function App() {
const [count, setCount] = React.useState(0);
console.log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
I know this has something to do with StrictMode but I'm not sure what. And also I've always been unclear about how strict mode works and what's its purpose, so I'd appreciate if anyone could highlight that as well.
TL;DR
When components are wrapped in StrictMode, React runs certain functions twice in order to help developers catch mistakes in their code.
And this happens both in React 18 and React 17 but the reason you aren't experiencing this with the latter is because in React 17, React automatically silences logs in the second call.
If you extract out console.log and use the extracted alias to log, then you would get similar behavior with both versions.
const log = console.log;
function App() {
const [count, setCount] = React.useState(0);
log(count);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Note:
In React 17, React automatically modifies the console methods like console.log() to silence the logs in the second call to lifecycle functions. However, it may cause undesired behavior in certain cases where a workaround can be used.
Starting from React 18, React does not suppress any logs. However, if you have React DevTools installed, the logs from the second call will appear slightly dimmed. React DevTools also offers a setting (off by default) to suppress them completely.
Source
Now let's dive deep to understand what actually happens in strict mode and how it can helpful.
Strict Mode
Strict Mode is a tool that helps identify coding patterns that may cause problems when working with React, like impure renders.
In Strict Mode in development, React runs the following functions twice:
Functional Components
Initializers
Updaters
And this is because your components, initializers & updaters need to be pure functions but if they aren’t then double-invoking them might help surface this mistake. And if they are pure, then the logic in your code is not affected in any manner.
Note: React uses the result of only one of the calls, and ignores the result of the other.
In the example below observe that components, initializers & updaters all run twice during development when wrapped in StrictMode (snippet uses the development build of React).
// Extracting console.log in a variable because we're using React 17
const log = console.log;
function App() {
const [count, setCount] = React.useState(() => {
log("Initializers run twice");
return 0;
});
log("Components run twice");
const handleClick = () => {
log("Event handlers don’t need to be pure, so they run only once");
setCount((count) => {
log("Updaters run twice");
return count + 1;
});
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Few notes from the above example:
You might have noticed that when you click the button for the first time the Updaters run twice log prints only once but on subsequent clicks it prints twice. But you can ignore this behavior and assume that it always prints twice but if you want more details about the same you can follow this github issue.
We had to extract console.log into a separate variable to get logs for both the invocations printed and this is because React 17 automatically silences logs for the second call (as mentioned in the TL;DR). If you update the CDN link to React 18, then this extraction wouldn't be required.
Calling the setCount updater function twice doesn’t mean that it would now increment the count twice on every click, no, because it calls the updater with the same state both the times. So, as long as your updaters are pure functions, your application wouldn’t get affected by the no. of times it’s called.
"Updaters" & "Initializers" are generic terms in React. State updaters & state initializers are just one amongst many. Other updaters are "callbacks" passed to useMemo and "reducers". Another initializers is useReducer initializer etc. And all of these should be pure functions so strict mode double invokes all of them. Checkout this example:
const logger = console.log;
const countReducer = (count, incrementor) => {
logger("Updaters [reducers] run twice");
return count + incrementor;
};
function App() {
const [count, incrementCount] = React.useReducer(
countReducer,
0,
(initCount) => {
logger("Initializers run twice");
return initCount;
}
);
const doubleCount = React.useMemo(() => {
logger("Updaters [useMemo callbacks] run twice");
return count * 2;
}, [count]);
return (
<div>
<p>Double count: {doubleCount}</p>
<button onClick={() => incrementCount(1)}>Increment</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
How is Strict Mode helpful?
Let's look at an example where Strict Mode would help us find a serious mistake.
// This example is in React 18 to highlight the fact that
// the double invocation behavior is similar in both React 17 & 18.
function App() {
const [todos, setTodos] = React.useState([
{ id: 1, text: "Learn JavaScript", isComplete: true },
{ id: 2, text: "Learn React", isComplete: false }
]);
const handleTodoCompletion = (todoId) => {
setTodos((todos) => {
console.log(JSON.stringify(todos));
return todos.map((todo) => {
if (todo.id === todoId) {
todo.isComplete = !todo.isComplete; // Mutation here
}
return todo;
});
});
};
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<span
style={{
textDecoration: todo.isComplete ? "line-through" : "none"
}}
>
{todo.text}
</span>
<button onClick={() => handleTodoCompletion(todo.id)}>
Mark {todo.isComplete ? "Incomplete" : "Complete"}
</button>
</li>
))}
</ul>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
What's the problem with the above example?
You would've noticed that the buttons don't work as expected, they don't toggle the isComplete boolean and the problem is that the updater function passed to setTodos is not a pure function as it mutates an object in the todos state. And since the updater is called twice, and it is not a pure function, the second call reverses the isComplete boolean back to it’s original value.
Note: It's only because of strict mode's double invocation that we were able to catch this mistake. If we opt out of strict mode, then the component would luckily work as expected but that doesn't mean the code is authored correctly, it only works because of how isolated the component is and in real world scenarios mutations like these can cause serious issues. And even if you luckily get away with such mutations, you might still encounter problems because currently the updater relies on the fact that it's only called once for every click but this is not something that React guarantees (with concurrency features in mind).
If you make the updater a pure function, it would solve the issue:
setTodos((todos) => {
logger(JSON.stringify(todos, null, 2));
return todos.map((todo) =>
todo.id === todoId ? { ...todo, isComplete: !todo.isComplete } : todo
);
});
What's new with Strict Mode in React 18
In React 18, StrictMode gets an additional behavior to ensure it's compatible with reusable state. When Strict Mode is enabled, React intentionally double-invokes effects (mount -> unmount -> mount) for newly mounted components. This is to ensure that a component is resilient to being "mounted" and "unmounted" more than once. Like other strict mode behaviors, React only does this for development builds.
Consider the example below (Source):
function App(props) {
React.useEffect(() => {
console.log("Effect setup code runs");
return () => {
console.log("Effect cleanup code runs");
};
}, []);
React.useLayoutEffect(() => {
console.log("Layout effect setup code runs");
return () => {
console.log("Layout effect cleanup code runs");
};
}, []);
console.log("React renders the component")
return <h1>Strict Effects In React 18</h1>;
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="root"></div>
The App component above declares some effects to be run on mount and unmount. Prior to React 18, the setup functions would only run once (after the component is initially mounted) and the cleanup functions would also run only once (after the component is unmounted). But in React 18 in StrictMode, the following would happen:
React renders the component (twice, nothing new)
React mounts the component
Layout effect setup code runs
Effect setup code runs
React simulates the component being hidden or unmounted
Layout effect cleanup code runs
Effect cleanup code runs
React simulates the component being shown again or remounted
Layout effect setup code runs
Effect setup code runs
Suggested Readings
ReactWG: How to support Reusable State in Effects
ReactWG: Adding Reusable State to StrictMode
React Docs: Strict Mode
Beta React Docs: My initializer or updater function runs twice
Beta React Docs: My reducer or initializer function runs twice
React 17 Strict Mode was also double rendering but it was suppressing the logs. That is why we were not seeing double logs. From react 17 docs:
Starting with React 17, React automatically modifies the console
methods like console.log() to silence the logs in the second call to
lifecycle functions. However, it may cause undesired behavior in
certain cases where a workaround can be used.
The difference of React 18 is it is now showing the double renderings. If you are using "React Developer Tools" chrome extension with, you can see that which logs are coming from strict mode:
One of the major additions to React 18 is Concurrency so Strict Mode will also help us see concurrency-related bugs during development.

Check when WebKit context is available in NW.js

When executed in Node context (node-main),
setTimeout(function () {
console.log(nw);
}, 20);
throws
nw is not defined
because WebKit context is not ready (right from the start window is unavailable in NW.js <= 0.12, window.nw in NW.js >= 0.13). And
setTimeout(function () {
console.log(nw);
}, 200);
works just fine but setTimeout looks like a hack, setting it to safe delay value may cause undesirable lag.
How can the availability of WebKit context and nw be checked from Node context? Is there a reasonable way, like an event that could be handled?
The following achieves the same thing but does it the other way around.
In your html file:
<body onload="process.mainModule.exports.init()">
In your node-main JS file:
exports.init = function() {
console.log(nw);
}
Here, init function is only called when Webkit context/DOM is available.
You could use pollit :) ...
var pit = require("pollit");
foo = function(data) {
console.log(nw);
};
pit.nw('nw', foo);
I've tested it and it works for me :). This modularizes the solution that I give near the end of this.
The nw object does not exist until webkit is up and running ie the browser
window has been created. This happens after Node starts up which is why you're
getting this error. To use the nw api you either create events that can be
listened to or call global functions the former being preferable. The following code will demonstrate both and should give you a good idea of how Node and WebKit are interfacing with each other.
This example creates a Window, opens devtools and allows you to toggle the
screen. It also displays the mouse location in the console. It also demonstrates how to send events using the DOM ie body.onclick() and attaching events from within Node ie we're going to catch minimize events and write them to the console.
For this to work you need to be using the SDK version of NW. This is my package.json
{
"name": "hello",
"node-main": "index.js",
"main": "index.html",
"window": {
"toolbar": true,
"width": 800,
"height": 600
},
"dependencies" : {
"robotjs" : "*",
"markdown" : "*"
}
}
The two files you need are index.html
<!DOCTYPE html>
<html>
<head>
<script>
var win = nw.Window.get();
global.win = win;
global.console = console;
global.main(nw);
global.mouse();
var markdown = require('markdown').markdown;
document.write(markdown.toHTML("-->Click between the arrows to toggle full screen<---"));
</script>
</head>
<body onclick="global.mouse();">
</body>
</html>
and index.js.
var robot = require("robotjs");
global.mouse = function() {
var mouse = robot.getMousePos();
console.log("Mouse is at x:" + mouse.x + " y:" + mouse.y);
global.win.toggleFullscreen();
}
global.main = function(nw_passed_in) {
global.win.showDevTools();
console.log("Starting main");
console.log(nw_passed_in);
console.log(nw);
global.win.on('minimize', function() {
console.log('n: Window is minimized from Node');
});
}
When running this I used
nwjs --enable-logging --remote-debugging-port=1729 ./
You can then open up the browser using
http://localhost:1729/
for debugging if needed.
If you want to do something as soon as the nw object exists you can poll it. I'd use eventEmitter, if you don't want to use event emitter you can just as easily wrap this in a function and call it recursively. The following will display how many milliseconds it took before the nw object was setup. On my system this ranged between 43 - 48 milliseconds. Using a recursive function was no different. If you add this to the code above you'll see everything logged to the console.
var start = new Date().getTime();
var events = require('events');
var e = new events.EventEmitter();
var stop = 0;
e.on('foo', function() {
if(typeof nw === 'undefined') {
setTimeout(function () {
e.emit('is_nw_defined');
}, 1);
}
else {
if(stop === 0) {
stop = new Date().getTime();
}
setTimeout(function () {
console.log(stop - start);
console.log(nw);
e.emit('is_nw_defined');
}, 2000);
}
});
e.emit('is_nw_defined');
Solution 1:
You can use onload, see reference.
main.js:
var gui = require("nw.gui"),
win = gui.Window.get();
onload = function() {
console.log("loaded");
console.log(win.nw);
};
index.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="main.js"></script>
</head>
<body></body>
</html>
package.json:
{
"name": "Freebox",
"main": "index.html"
}
Solution 2:
(To prevent issue, but it is not necessary).
var gui = require("nw.gui"),
win = gui.Window.get();
onload = function() {
console.log("loaded");
var a = function () {
if (!win.nw) return setTimeout(a, 10);
console.log(win.nw);
};
};
The solution I've initially come up looks like
app-node.js
process.once('webkit', () => {
console.log(nw);
});
app.html
<html>
<head>
<script>
global.process.emit('webkit');
</script>
...
I would be glad to know that there is already an event to listen, so cross-platform client scripts could omit NW-related code.

I am not able to access variable 'dps' declare in index.js within index.html , using npm start

I am not able to access variable 'dps' declare in index.js within index.html , using npm start(for starting electron app)
I am able to access my sql and get data in index.js and i want to show that on index.html (used nodeJs and Electron)
'dps' refers to js object which has mysql data
//My index.js file has
var app = require('app');
var dps = [{x:1,y:2}];
// Module to create native browser window.
var BrowserWindow = require('browser-window');
var mainWindow = null;
var dps = [{x:1,y:2}];
var mysql = require('mysql');
// Quit when all windows are closed.
app.on('window-all-closed', function () {
if (process.platform != 'darwin') {
app.quit();
}
});
app.on('ready', function () {
// Create the browser window.
mainWindow = new BrowserWindow({ width: 800, height: 600 });
mainWindow.loadUrl('file://' + __dirname + '/index.html');
// Open the devtools.
// mainWindow.openDevTools();
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
});
//My html file
<html>
<head>
<!--<script type="text/javascript" src = "index.js"/>-->
<script type="text/javascript" src="index.js"></script>
<script type="text/javascript">
alert(dps); --- not getting dps value here(/anywhere in html)
</head>
</html>
Your code in index.html runs in a different process (usually called the Renderer process) than your in index.js (which runs in the Main process). Furthermore, your index.js file is a module, therefore, even if the two were run by the same process, you'd have to export your dps variable or make it a global variable like global.dps (however, that's a bad practice!).
There are a number of ways to share date between the Main and the Renderer process; Electron provides the ipcMain, ipcRenderer and remote modules for that purpose; you can also pass data from Main to the Renderer using URL encoded parameters (but this doesn't help you if the data changes); finally, you can use any other form of IPC (e.g., send messages over a socket, use shared memory or shared files, etc.) -- but it's a good idea to start with Electron's ipc or remote modules.
Having said that, in your case, the consensus seems to be not to use the Main process for DB access only to then pass the information on to the Renderer, but rather access the DB directly from the Renderer; that way you won't have to worry about IPC at all (at least in this case).

phantomjs and requirejs

codes in file main.js is like this:
phantom.injectJs("libs/require-1.0.7.js");
require.config(
{
baseUrl: ""
}
);
require([], function(){});
when i run "phantomjs main.js" in the commandline, requirejs doesn't work well in the main.js. I know how to use requirejs in the page running in the browser(including phantomjs' way: page.open(url, callback)), but not like above. I tries using requirejs like the main.js, it is a popular problem, i think. Thank you!
I just struggled for some time. My solution is not clean, but it works, and I'm happy with that due to the unfinished api documentation from phantomjs.
Wordy explanation
You need three files. One is your amd phantomjs test file which I'll call "amd.js". The second is your html page to load which I'll name "amd.html". Finally the browser test which I called "amdTestModule.js".
In amd.html, declare your script tag per normal:
<script data-main="amdTestModule.js" src="require.js"></script>
In your phantomjs test file, this is where it gets hacky. Create your page, and load in the 'fs' module. This allows you to open a relative file path.
var page = require('webpage').create();
var fs = require('fs');
page.open('file://' + fs.absolute('tests/amd.html'));
Now since requirejs loads files asynchronously, we can't just pass in a callback into page.open and expect things to go smoothly. We need some way to either
1) Test our module in the browser and communicate the result back to our phantomjs context. Or
2) Tell our phantomjs context that upon loading all the resources, to run a test.
#1 was simpler for my case. I accomplished this via:
page.onConsoleMessage = function(msg) {
msg = msg.split('=');
if (msg[1] === 'success') {
console.log('amd test successful');
} else {
console.log('amd test failed');
}
phantom.exit();
};
**See full code below for my console.log message.
Now phantomjs apparently has an event api built in but it is undocumented. I was also successfully able to get request/response messages from their page.onResourceReceived and page.onResourceRequested - meaning you can debug when all your required modules are loaded. To communicate my test result however, I just used console.log.
Now what happens if the console.log message is never ran? The only way I could think of resolving this was to use setTimeout
setTimeout(function() {
console.log('amd test failed - timeout');
phantom.exit();
}, 500);
That should do it!
Full Code
directory structure
/projectRoot
/tests
- amd.js
- amdTestModule.js
- amd.html
- require.js (which I symlinked)
- <dependencies> (also symlinked)
amd.js
'use strict';
var page = require('webpage').create();
var fs = require('fs');
/*
page.onResourceRequested = function(req) {
console.log('\n');
console.log('REQUEST');
console.log(JSON.stringify(req, null, 4));
console.log('\n');
};
page.onResourceReceived = function(response) {
console.log('\n');
console.log('RESPONSE');
console.log('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response, null, 4));
console.log('\n');
};
*/
page.onConsoleMessage = function(msg) {
msg = msg.split('=');
if (msg[1] === 'success') {
console.log('amd test successful');
} else {
console.log('amd test failed');
}
phantom.exit();
};
page.open('file://' + fs.absolute('tests/amd.html'));
setTimeout(function() {
console.log('amd test failed - timeout');
phantom.exit();
}, 500);
amd.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script data-main='amdTestModule.js' src='require.js'></script>
</body>
</html>
amdTestModule.js
require([<dependencies>], function(<dependencies>) {
...
console.log(
(<test>) ? "test=success" : "test=failed"
);
});
console
$ phantomjs tests/amd.js
amd test successful
you are misunderstanding webpage.injectJs()
it's for injecting scripts into the page you are loading, not into the phantomjs runtime environment.
So using .injectJs() is making requirejs load up into your page, not into phantomjs.exe.
That said, phantomjs's runtime environment has an aproximation of commonjs. RequireJs will not run on there by default. If you felt especially (VERY) motivated, you could attempt porting the require-shim made for nodejs, but it doesn't work out of the box, and would require an incredibly deep understanding of the runtimes. for more details: http://requirejs.org/docs/node.html
a better idea:
probably you should make sure you have commonjs versions of your javascript you wish to run. i personally write my code in typescript so i can build for either commonjs or amd. i use commonjs for phantomjs code, and amd for nodejs and browser.

How to activate javascript links with zombie.js

I am trying to get get zombie.js to activate a link that uses javascript. The page I am testing it on is:
<html>
<body>
<div id="test123">
START_TEXT
</div>
GO<br/>
<script type="text/javascript">
go = function() {
var el = document.getElementById("test123");
el.innerHTML = "CHANGED";
}
</script>
</body>
</html>
The Script I am using is:
var zombie = require("zombie");
var browser = new zombie.Browser;
browser.visit( "http://localhost:8000/testpage.html",
function() {
browser.clickLink("GO", function(e, browser, status) {
var temp = browser.text("div#test123");
console.log("content:", temp);
});
});
I get the error message:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot load resource: javascript:go()
at History._resource (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:75:15)
at History._pageChanged (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:60:21)
at History._assign (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:213:19)
at Object.location (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:51:24)
at Object.click (/home/julian/temp/node_modules/zombie/lib/zombie/jsdom_patches.coffee:31:59)
at Object.dispatchEvent (/home/julian/temp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:480:47)
at /home/julian/temp/node_modules/zombie/lib/zombie/eventloop.coffee:130:16
at EventLoop.perform (/home/julian/temp/node_modules/zombie/lib/zombie/eventloop.coffee:121:7)
at EventLoop.dispatch (/home/julian/temp/node_modules/zombie/lib/zombie/eventloop.coffee:129:19)
at Browser.dispatchEvent (/home/julian/temp/node_modules/zombie/lib/zombie/browser.coffee:220:30)
When I use
browser.evaluate("go()")
it works.
What am I missing?
Zombie.js doesn't handle links with "javascript:" on href (at the time of this writing).
I fixed it by adding 3 lines to the source code. Look for /node_modules/zombie/lib/zombie/history.coffee and add the 3 lines commented with FIX (beware that in .coffee you must respect indentation, ie. use 2 spaces):
# Location uses this to move to a new URL.
_assign: (url)->
url = #_resolve(url)
# FIX: support for javascript: protocol href
if url.indexOf("javascript:")==0
#_browser.evaluate(url.substr("javascript:".length))
return
was = #_stack[#_index]?.url # before we destroy stack
#_stack = #_stack[0..#_index]
#_stack[++#_index] = new Entry(this, url)
#_pageChanged was
I probably should fork zombie.js on github.com and put this into a Pull Request, but until then you are welcome to do use this snippet, or make that pull request before me.
Well it doesn't seem to understand javascript:code hrefs. Perhaps you can get the url, remove the javascript:-section and evaluate it?
Disclaimer: I haven't used zombie.js myself yet.
The zombie.js API says it takes a CSS selector or the link text as the first parameter for browser.clickLink(). So your code should work.
But try adding an id to the link, if you have control over the page, and using a CSS selector
browser.clickLink('#thelink', function(e, browser, status) {
var temp = browser.text("div#test123");
console.log("content:", temp);
});

Resources