mocha-casperjs headless testing of a multi-client node.js app - javascript

I have a node.js application that runs on the Sails.js MVC Framework. The application supports connections from multiple clients using socket.io in real-time. It supports different roles. For example, you can login as a standard user or as a moderator.
For testing, I use mocha-casperjs (and chai and other stuff that is not important for my question) and run the tests from grunt using grunt mocha_phantomjs. The tests are specified in the Gruntfile.js file in the root folder of my project. They are specified like this:
Gruntfile.js:
mocha_casperjs: {
files: {
src: [
'test/single-client.js',
'test/multi-client.js',
]
}
}
Here is a test case I want to verify: Once the Moderator clicks on a specific button, a div.container should appear on both the Moderator's view as well as the standard user's view.
Now, testing the single-client aspect works well. Here is a basic scenario:
test/single-client.js:
describe('Clicking on a Button as a Moderator', function() {
it('results in div.container to appear on the Moderators view', function() {
casper
.start('http://localhost:1337')
.logInAsMod() //Note: This is pseudocode for submitting a login form
.thenClick('button')
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
})
}
which results in:
Clicking on a Button as a Moderator
✓ results in div.container to appear on the Moderator's view
However, I'm struggling in testing the multi-client aspect of this test case: The div.container should not only appear on the Moderator's view, but also on the standard user's view. From what I understand, the casper.run() method, which starts the tests defined earlier in the source code, will be called by the mocha-phantomjs module. So something like this won't work:
test/multi-client.js:
describe('Clicking on a Button as a Moderator', function() {
it('results in div.container to appear on the Moderators view and on the standard users view', function() {
casper
.start('http://localhost:1337')
.logInAsMod() //Note: This is pseudocode for submitting a login form
.thenClick('button')
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
casper
.logInAsUser() //Note: This is pseudocode for submitting a login form
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
})
}
The problem with the code above is that the casper.run() call only creates one instance of casperjs. It would be really great if I could write something like this:
var casperMod = require('casper').create();
var casperUser = require('casper').create();
casperMod
.start('http://localhost:1337')
.logInAsMod() //Note: This is pseudocode for submitting a login form
casperUser
.start('http://localhost:1337')
.logInAsUser() //Note: This is pseudocode for submitting a login form
casperMod
.thenClick('button')
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
casperUser
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
So I would have two instances of casperjs that I can write the routines for. However, this results in Error: Cannot find module 'casper' because the mocha-casperjs framework does not support the inclusion of another casperjs instance.
Permanently logging out and in again is not an option, because I want to test the real-time aspect of the application.
Does anyone have a suggestion in how to achieve multiple instances of casperjs when using mocha-casperjs?

You will have problems with this in any framework as you need two sets of cookies, basically two browsers running. Even if you did create two casper instances, you're still in one phantomjs process that shares the cookies.
What can do is create a new WebPage instance and swap that out on the current casper instance, plus swap the cookies:
casper
.start('http://localhost:1337')
.logInAsMod()
.thenClick('button')
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
.then(function() {
var userPage = require('webpage').create(),
modCookies = phantom.cookies
phantom.clearCookies()
casper
.logInAsUser()
.then(function() {
('div.container').should.be.inDOM.and.visible;
})
})

Related

Enable users of a WebRtc app to download webrtc logs via javascript

I've seen the following:
chrome://webrtc-internals
However I'm looking for a way to let users click a button from within the web app to either download or - preferably - POST WebRtc logs to an endpoint baked into the app. The idea is that I can enable non-technical users to share technical logs with me through the click of a UI button.
How can this be achieved?
Note: This should not be dependent on Chrome; Chromium will also be used as the app will be wrapped up in Electron.
You need to write a javascript equivalent that captures all RTCPeerConnection API calls. rtcstats.js does that but sends all data to a server. If you replace that behaviour with storing it in memory you should be good.
This is what I ended up using (replace knockout with underscore or whatever):
connectionReport.signalingState = connection.signalingState;
connectionReport.stats = [];
connection.getStats(function (stats) {
const reportCollection = stats.result();
ko.utils.arrayForEach(reportCollection, function (innerReport) {
const statReport = {};
statReport.id = innerReport.id;
statReport.type = innerReport.type;
const keys = innerReport.names();
ko.utils.arrayForEach(keys, function (reportKey) {
statReport[reportKey] = innerReport.stat(reportKey);
})
connectionReport.stats.push(statReport);
});
connectionStats.push(connectionReport);
});
UPDATE:
It appears that this getStats mechanism is soon-to-be-deprecated.
Reading through js source of chrome://webrtc-internals, I noticed that the web page is using a method called chrome.send() to send messages like chrome.send('enableEventLogRecordings');, to execute logging commands.
According to here:
chrome.send() is a private function only available to internal chrome
pages.
so the function is sandboxed which makes accessing to it not possible

Meteor: Authenticating Chrome Extension via DDP

I've built a Chrome Extension that takes a selection of text and when I right click and choose the context menu item, it sends that text to my Meteor app. This works fine, however, I can't figure out the process of using Oauth to authenticate users.
I'm using this package: https://github.com/eddflrs/meteor-ddp
Here is the JS within background.js (for Chrome Extension):
var ddp = new MeteorDdp("ws://localhost:3000/websocket");
ddp.connect().then(function() {
ddp.subscribe("textSnippets");
chrome.runtime.onMessage.addListener(function(message) {
ddp.call('transferSnippet', ['snippetContent', 'tag', snippetString]);
});
});
Here is the relevant portion of my other JS file within my Chrome Extension:
function genericOnClick(info) {
snippetString = [];
snippetString.push(info.selectionText);
var snippetTag = prompt('tag this thing')
snippetString.push(snippetTag);
chrome.runtime.sendMessage(snippetString);
}
And here is the relevant portion of my Meteor app:
'transferSnippet': function(field1, field2, value1, value2) {
var quickObject = {};
quickObject.field1 = value1[0];
quickObject.field2 = value1[1];
TextSnippets.insert({
snippetContent: value1[0],
tag: value1[1]
});
}
Basically I'm stuck and don't know how to go about making a DDP call that will talk to my Meteor app in order to authenticate a user
This question is a bit old, but if anyone is still looking for a solution. I had a similar problem that I was able to solve using the following plugin: https://github.com/mondora/asteroid. Here is an example of how to do it for twitter oauth:
https://github.com/mondora/asteroid/issues/41#issuecomment-72334353

In Meteor, how to only load a javascript when the user is logged in using an particular external service?

What I really need is to load google apis client library for javascript when a user is logged in using google service. If a user is logged in using password or other external services, the library is not loaded.
Is this possible by any means?
Thanks.
If you want to load the library from an external URL after the user has logged in, you could try something like this (put this anywhere in your client code):
Tracker.autorun(function(c) {
var user = Meteor.user();
// replace this with the appropriate check for a google account
if (user && user.profile && user.profile.isGoogle) {
// stop the autorun now that the user is logged in
c.stop();
// load the api library
$.ajax({
url: '//url-to-fancy-google-api.google.com',
dataType: 'script',
cache: true
});
}
});
It is possible, use anti:modules package. First add it to the app:
meteor add anti:modules
Then create /layers folder inside your project and place your optional files in its subfolder:
/
layers
googleUser
someFile.module.js
anotherFile.module.js
...
Then in your code create a global module:
theApp = Module('$global').as('myApp');
and load it when necessary:
theApp.require('googleUser', function () {
console.log('googleUser code loaded');
});

sammy.js url not found cause of subdirectory

Hi I create a spa with knockout, amplify and sammy.
If I now click an a link like:
#/page?s=About
it links to url.de/subdirectory/#/page?s=About which is right but the console fires following error:
GET url.de/About 404 (Not Found)
because it should be:
url.de/subdirectory/About
My sammy code is:
var app=$.sammy(function () {
// define prexecutes
// update parameters in appModel from request for all routes
this.before('', function() {
//setParameters(this);
});
// authenticate on any page except for login and logout routes
this.before({except: {path:/\/(login|logout|hp)/}}, function() {
});
// actual routes
// home
this.get('#/', function() {
appModel.page("home");
return false;
});
// content
this.get('#/page', function(eventContext) {
content(eventContext);
});
});
app.run('#/');
How do I get sammy not ignoring my subdirectory in which my site is?
You can try with another custom route definition like this
this.get('#/page/:s', function(eventContext) { // where s is parametre
content(eventContext);
});
also use url like this
#/page/About // it will read 'About' as value of parameter 's'
instead of
#/page?s=About
Hope this helps
I ran into a similar situation myself recently. I could not find any configuration options or workarounds that would allow Sammy.js to route to a subdirectory. My solution was to create a virtual host on the server hosting my app with the subdirectory as the subdomain in order to place the app at the document root. In your case, the virtual server would map from url.de/subdir/About to subdir.url.de/About. I realize that this may not be possible for you (since I don't know how much control you have over your hosting environment), but it got me moving again pretty quickly. Hope this helps.
P.S. As a small aside, I just browsed through the Sammy.js source at https://github.com/quirkey/sammy, and it appears that the subdirectory is stripped out (in error) to fix an IE quirk. https://github.com/quirkey/sammy/blob/master/lib/sammy.js#L301 may be the problem.

Web site not working as expected with CasperJS

I'm using CasperJS 1.0.2 under PhantomJS 1.8.1 on Windows 8.
Trying to write a test for a web site. The site is heavily reliant on JS and the coding principals are quite unusual, which may be creating some problems but I'm not sure.
Here is the code I'm using to test login and search function:
var url = 'http://www.testsite.com/';
var casper = require('casper').create();
casper.start();
casper.start(url, function() {
this.echo('Page: ' + this.getTitle());
this.capture('start.png');
if (this.exists('input#TxtUserName')) {
this.sendKeys('input#TxtUserName', 'testlogin');
this.sendKeys('input#TxtPassword', 'testpass');
this.click('input#BtnLogin');
this.capture('loggedin.png');
}
});
casper.then(function() {
this.capture('beforesearch.png');
this.sendKeys("input#txtSearch", '1002');
this.click("input#cmdSubmit");
this.echo('Searching');
this.capture('aftersearch.png');
});
casper.run();
When I run this code, every page on the screen capture is the same with the exception that the login information is filled in on login.png. At no point does it actually login (using my real login credentials) after the click event. The search results also don't show after that click is fired.
Any clue what could be causing this?
Here is my waitFor code after submitting the search:
casper.waitForText("Part:", function() {
this.capture('searchresults.png');
});
You should use casper.waitFor to make sure the next page has been loaded. Otherwise phantom will take the screenshot before the form submit has been answered.

Categories

Resources