I am trying to run Jasmine inside PhantomJS. After a lot of effort I am down to:
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js");
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine-html.js");
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js");
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/console.js");
var result = page.evaluate(function (done) {
var ConsoleReporter = jasmineRequire.ConsoleReporter();
var options = {
timer: new jasmine.Timer,
print: function () {
console.log.apply(console, arguments)
}
};
consoleReporter = new ConsoleReporter(options);
jasmine.getEnv().addReporter(consoleReporter);
describe("test1", function () {
console.log("in test 1");
it("should do something", function () {
console.log("NEVER GETS HERE"); // <-- never gets there
});
});
});
The code executes all the way up to it() but the callback never executes :|
[edit]
I'm trying to use phantom with jasmine for end to end testing. I already have an app server and I'm using Karma for unit tests. So I don't think a testRunner.html is going to help. PhantomJS should login to my app and do some stuff, which I will test with Jasmine.
Got it with the dumb luck brute force approach. I never found helpful documentation but this helped a little
My files are like this:
testrunner.js
var system = require("system");
var page = require("webpage").create();
var loginUrl = system.args[1];
page.onConsoleMessage = function (msg) {
console.log("FROM PAGE: " + msg);
};
page.onError = function (err) {
console.log("ERR FROM PAGE: " + JSON.stringify(err));
}
var loggedIn = false;
function onLogin_atHomePage() {
if (loggedIn)
return; // dont run twice if a developer is programatically logging in
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/jasmine.js");
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/boot.js");
page.injectJs("/jasmine-standalone-2.0.0/lib/jasmine-2.0.0/console.js");
var result = page.evaluate(function (done) {
var ConsoleReporter = jasmineRequire.ConsoleReporter();
var options = {
timer: new jasmine.Timer,
print: function () {
console.log.apply(console, arguments)
}
};
consoleReporter = new ConsoleReporter(options); // initialize ConsoleReporter
jasmine.getEnv().addReporter(consoleReporter); //add reporter to execution environment
describe("test1", function () {
console.log("in test 1");
it("should do something", function () {
console.log("NEV");
});
});
jasmine.getEnv().execute();
});
}
page.open(loginUrl, function () {
console.log("LOGGING IN...");
page.onLoadFinished = onLogin_atHomePage;
page.evaluate(function () {
$(document).ready(function () {
var $u = $("#login_id");
var $p = $("[name='password']");
$u.val("me");
$p.val("666");
submitForm();
});
});
});
boot.js
/**
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
*/
(function() {
/**
* ## Require & Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
window.jasmine = jasmineRequire.core(jasmineRequire);
/**
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
*/
//jasmineRequire.html(jasmine);
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
/**
* ## The Global Interface
*
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
*/
var jasmineInterface = {
describe: function(description, specDefinitions) {
return env.describe(description, specDefinitions);
},
xdescribe: function(description, specDefinitions) {
return env.xdescribe(description, specDefinitions);
},
it: function(desc, func) {
return env.it(desc, func);
},
xit: function(desc, func) {
return env.xit(desc, func);
},
beforeEach: function(beforeEachFunction) {
return env.beforeEach(beforeEachFunction);
},
afterEach: function(afterEachFunction) {
return env.afterEach(afterEachFunction);
},
expect: function(actual) {
return env.expect(actual);
},
pending: function() {
return env.pending();
},
spyOn: function(obj, methodName) {
return env.spyOn(obj, methodName);
},
jsApiReporter: new jasmine.JsApiReporter({
timer: new jasmine.Timer()
})
};
/**
* Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
*/
if (typeof window == "undefined" && typeof exports == "object") {
extend(exports, jasmineInterface);
} else {
extend(window, jasmineInterface);
}
/**
* Expose the interface for adding custom equality testers.
*/
jasmine.addCustomEqualityTester = function(tester) {
env.addCustomEqualityTester(tester);
};
/**
* Expose the interface for adding custom expectation matchers
*/
jasmine.addMatchers = function(matchers) {
return env.addMatchers(matchers);
};
/**
* Expose the mock interface for the JavaScript timeout functions
*/
jasmine.clock = function() {
return env.clock;
};
/**
* ## Runner Parameters
*
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
/*var queryString = new jasmine.QueryString({
getWindowLocation: function() { return window.location; }
});
var catchingExceptions = queryString.getParam("catch");
env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
*/
/**
* ## Reporters
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
*/
/*
var htmlReporter = new jasmine.HtmlReporter({
env: env,
onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); },
getContainer: function() { return document.body; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
timer: new jasmine.Timer()
});
*/
/**
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
*/
env.addReporter(jasmineInterface.jsApiReporter);
//env.addReporter(htmlReporter);
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
/*var specFilter = new jasmine.HtmlSpecFilter({
filterString: function() { return queryString.getParam("spec"); }
});
env.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
};*/
/**
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
*/
window.setTimeout = window.setTimeout;
window.setInterval = window.setInterval;
window.clearTimeout = window.clearTimeout;
window.clearInterval = window.clearInterval;
/**
* ## Execution
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
env.execute();
};
/**
* Helper function for readability above.
*/
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
}
}());
Related
I was trying to use the default template in a rest query using this:
/company/somecompanyid?template=default
But I still get all the data from my mongodb including fields and collections of related tables that are not in the templates.
This is also defined as the defaultTemplate in the model but it doesn't seems to impact the results in any way.
Can someone explain what I am doing wrong and how to apply the template?
If I want to include just the object Id rather than a whole related object in the reply, how do I specify it in the template?
company.js:
'use strict';
/**
* Module dependencies
*/
// Node.js core.
const path = require('path');
// Public node modules.
const _ = require('lodash');
const anchor = require('anchor');
// Local dependencies.
const WLValidationError = require('../../../node_modules/waterline/lib/waterline/error/WLValidationError');
// Settings for the Company model.
const settings = require('./Company.settings.json');
/**
* Export the Company model
*/
module.exports = {
/**
* Basic settings
*/
// The identity to use.
identity: settings.identity,
// The connection to use.
connection: settings.connection,
// Do you want to respect schema?
schema: settings.schema,
// Limit for a get request on the list.
limit: settings.limit,
// Merge simple attributes from settings with those ones.
attributes: _.merge(settings.attributes, {
}),
// Do you automatically want to have time data?
autoCreatedAt: settings.autoCreatedAt,
autoUpdatedAt: settings.autoUpdatedAt,
/**
* Lifecycle callbacks on validate
*/
// Before validating value
beforeValidate: function (values, next) {
// WARNING: Don't remove this part of code if you don't know what you are doing
const api = path.basename(__filename, '.js').toLowerCase();
if (strapi.api.hasOwnProperty(api) && _.size(strapi.api[api].templates)) {
const template = strapi.api[api].templates.hasOwnProperty(values.template) ? values.template : strapi.models[api].defaultTemplate;
const templateAttributes = _.merge(_.pick(strapi.models[api].attributes, 'lang'), strapi.api[api].templates[template].attributes);
// Handle Waterline double validate
if (_.size(_.keys(values)) === 1 && !_.includes(_.keys(templateAttributes), _.keys(values)[0])) {
next();
} else {
const errors = {};
// Set template with correct value
values.template = template;
values.lang = _.includes(strapi.config.i18n.locales, values.lang) ? values.lang : strapi.config.i18n.defaultLocale;
_.forEach(templateAttributes, function (rules, key) {
if (values.hasOwnProperty(key)) {
// Check rules
const test = anchor(values[key]).to(rules);
if (test) {
errors[key] = test;
}
} else if (rules.required) {
errors[key] = [{
rule: 'required',
message: 'Missing attributes ' + key
}];
}
});
// Go next step or not
_.isEmpty(errors) ? next() : next(new WLValidationError({
invalidAttributes: errors,
model: api
}));
}
} else if (strapi.api.hasOwnProperty(api) && !_.size(strapi.api[api].templates)) {
next();
} else {
next(new Error('Unknow API or no template detected'));
}
}
/**
* Lifecycle callbacks on create
*/
// Before creating a value.
// beforeCreate: function (values, next) {
// next();
// },
// After creating a value.
// afterCreate: function (newlyInsertedRecord, next) {
// next();
// },
/**
* Lifecycle callbacks on update
*/
// Before updating a value.
// beforeUpdate: function (valuesToUpdate, next) {
// next();
// },
// After updating a value.
// afterUpdate: function (updatedRecord, next) {
// next();
// },
/**
* Lifecycle callbacks on destroy
*/
// Before updating a value.
// beforeDestroy: function (criteria, next) {
// next();
// },
// After updating a value.
// afterDestroy: function (destroyedRecords, next) {
// next();
// }
};
the template which is a subset of the company attributes (CompanyDefault.template.json):
{
"default": {
"attributes": {
"name": {
"required": true
},
"address": {},
"phone": {},
"email": {}
},
"displayedAttribute": "name"
}
}
Can you show us your default template file? Then, to use the template system, you have to generate your model from the Studio or the CLI. The template logic is located in the "/api/company/models/Company.js" file in your case. It will be interesting to show us this file too.
You're using the right way to apply the template. Your URL seems good! However, this is not possible to include only the object ID rather than the whole related object for now. To do this, I advise you to use GraphQL, or an RAW query...
In the V2, we will support the JSON API specifications in Strapi, this will fit with your problematics for sure!
PS: I'm one of the authors of Strapi.
I found this working example of Inheritance Patterns that separates business logic and framework code. I'm tempted to use it as a boilerplate, but since it is an inheritance Pattern, then how can I extend the business logic (the methods in var Speaker)?
For instance, how can I extend a walk: method into it?
/**
* Object Speaker
* An object representing a person who speaks.
*/
var Speaker = {
init: function(options, elem) {
// Mix in the passed in options with the default options
this.options = $.extend({},this.options,options);
// Save the element reference, both as a jQuery
// reference and a normal reference
this.elem = elem;
this.$elem = $(elem);
// Build the dom initial structure
this._build();
// return this so we can chain/use the bridge with less code.
return this;
},
options: {
name: "No name"
},
_build: function(){
this.$elem.html('<h1>'+this.options.name+'</h1>');
},
speak: function(msg){
// You have direct access to the associated and cached jQuery element
this.$elem.append('<p>'+msg+'</p>');
}
};
// Make sure Object.create is available in the browser (for our prototypal inheritance)
// Courtesy of Papa Crockford
// Note this is not entirely equal to native Object.create, but compatible with our use-case
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {} // optionally move this outside the declaration and into a closure if you need more speed.
F.prototype = o;
return new F();
};
}
$.plugin = function(name, object) {
$.fn[name] = function(options) {
// optionally, you could test if options was a string
// and use it to call a method name on the plugin instance.
return this.each(function() {
if ( ! $.data(this, name) ) {
$.data(this, name, Object.create(object).init(options, this));
}
});
};
};
// With the Speaker object, we could essentially do this:
$.plugin('speaker', Speaker);
Any ideas?
How about simply using JavaScript's regular prototype inheritance?
Consider this:
function Speaker(options, elem) {
this.elem = $(elem)[0];
this.options = $.extend(this.defaults, options);
this.build();
}
Speaker.prototype = {
defaults: {
name: "No name"
},
build: function () {
$('<h1>', {text: this.options.name}).appendTo(this.elem);
return this;
},
speak: function(message) {
$('<p>', {text: message}).appendTo(this.elem);
return this;
}
};
Now you can do:
var pp = new Speaker({name: "Porky Pig"}, $("<div>").appendTo("body"));
pp.speak("That's all folks!");
Speaker.prototype.walk = function (destination) {
$('<p>', {
text: this.options.name + " walks " + destination + ".",
css: { color: "red" }
}).appendTo(this.elem);
return this;
}
pp.walk("off the stage");
Runnable version:
function Speaker(options, elem) {
this.elem = $(elem)[0];
this.options = $.extend(this.defaults, options);
this.build();
}
Speaker.prototype = {
defaults: {
name: "No name"
},
build: function () {
$('<h1>', {text: this.options.name}).appendTo(this.elem);
return this;
},
speak: function(message) {
$('<p>', {text: message}).appendTo(this.elem);
return this;
}
};
var pp = new Speaker({name: "Porky Pig"}, $("<div>").appendTo("body"));
pp.speak("That's all folks!");
Speaker.prototype.walk = function (destination) {
$('<p>', {
text: this.options.name + " walks " + destination + ".",
css: { color: "red" }
}).appendTo(this.elem);
return this;
}
pp.walk("off the stage");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
Currently I use addon sdk to develop a firefox extension,which can redirect some request without loading them.
For example, a page refer a.js (maybe have something like this <script src='example.com/a.js'>),
then, I want to redirect this request to example2.com/b.js
Now,with nsIContentPolicy I can block example.com/a.js,
But I don't know how to load example2.com/b.js at the time when a.js is blocked.
my codes now look like this:
const { Cc, Ci } = require("chrome");
const xpcom = require("sdk/platform/xpcom");
const { Class } = require("sdk/core/heritage");
exports.gcleaner = Class({
extends: xpcom.Unknown,
interfaces: ["nsIContentPolicy"],
shouldLoad: function (contType, contLoc, reqOrig, ctx, typeGuess, extra) {
//https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy#shouldLoad()
if (contType === 2) //2 means js
{
var blockReg = new RegExp('example.com/a.js');
if (contLoc['spec'].match(blockReg))
{
return Ci.nsIContentPolicy.REJECT;
};
};
return Ci.nsIContentPolicy.ACCEPT;
},
shouldProcess: function (contType, contLoc, reqOrig, ctx, mimeType, extra) {
return Ci.nsIContentPolicy.ACCEPT;
}
});
let factory = xpcom.Factory({
Component: exports.gcleaner,
description: "hello world",
contract: "#liujiacai.net/gcleaner"
});
var catman = Cc["#mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
catman.addCategoryEntry("content-policy", "speeding.gcleaner", factory.contract, false, true);
Thanks!
I have a sdk module which does this here
https://github.com/jetpack-labs/pathfinder/blob/master/lib/redirect.js
Currently, I have a function that sometimes return an object with some functions inside. When using expect(...).toEqual({...}) it doesn't seem to match those complex objects. Objects having functions or the File class (from input type file), it just can't. How to overcome this?
Try the Underscore _.isEqual() function:
expect(_.isEqual(obj1, obj2)).toEqual(true);
If that works, you could create a custom matcher:
this.addMatchers({
toDeepEqual: function(expected) {
return _.isEqual(this.actual, expected);
};
});
You can then write specs like the following:
expect(some_obj).toDeepEqual(expected_obj);
As Vlad Magdalin pointed out in the comments, making the object to a JSON string, it can be as deep as it is, and functions and File/FileList class. Of course, instead of toString() on the function, it could just be called 'Function'
function replacer(k, v) {
if (typeof v === 'function') {
v = v.toString();
} else if (window['File'] && v instanceof File) {
v = '[File]';
} else if (window['FileList'] && v instanceof FileList) {
v = '[FileList]';
}
return v;
}
beforeEach(function(){
this.addMatchers({
toBeJsonEqual: function(expected){
var one = JSON.stringify(this.actual, replacer).replace(/(\\t|\\n)/g,''),
two = JSON.stringify(expected, replacer).replace(/(\\t|\\n)/g,'');
return one === two;
}
});
});
expect(obj).toBeJsonEqual(obj2);
If anyone is using node.js like myself, the following method is what I use in my Jasmine tests when I am only concerned with comparing the simple properties while ignoring all functions. This method requires json-stable-stringify which is used to sort the object properties prior to serializing.
Usage:
var stringify = require('json-stable-stringify');
var obj1 = {
func: function() {
},
str1: 'str1 value',
str2: 'str2 value',
nest1: {
nest2: {
val1:'value 1',
val2:'value 2',
someOtherFunc: function() {
}
}
}
};
var obj2 = {
str2: 'str2 value',
str1: 'str1 value',
func: function() {
},
nest1: {
nest2: {
otherFunc: function() {
},
val2:'value 2',
val1:'value 1'
}
}
};
it('should compare object properties', function () {
expect(stringify(obj1)).toEqual(stringify(obj2));
});
Extending #Vlad Magdalin's answer, this worked in Jasmine 2:
http://jasmine.github.io/2.0/custom_matcher.html
beforeEach(function() {
jasmine.addMatchers({
toDeepEqual: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var result = {};
result.pass = _.isEqual(actual, expected);
return result;
}
}
}
});
});
If you're using Karma, put that in the startup callback:
callback: function() {
// Add custom Jasmine matchers.
beforeEach(function() {
jasmine.addMatchers({
toDeepEqual: function(util, customEqualityTesters) {
return {
compare: function(actual, expected) {
var result = {};
result.pass = _.isEqual(actual, expected);
return result;
}
}
}
});
});
window.__karma__.start();
});
here's how I did it using the Jasmine 2 syntax.
I created a customMatchers module in ../support/customMatchers.js (I like making modules).
"use strict";
/**
* Custom Jasmine matchers to make unit testing easier.
*/
module.exports = {
// compare two functions.
toBeTheSameFunctionAs: function(util, customEqualityTesters) {
let preProcess = function(func) {
return JSON.stringify(func.toString()).replace(/(\\t|\\n)/g,'');
};
return {
compare: function(actual, expected) {
return {
pass: (preProcess(actual) === preProcess(expected)),
message: 'The functions were not the same'
};
}
};
}
}
Which is then used in my test as follows:
"use strict";
let someExternalFunction = require('../../lib/someExternalFunction');
let thingBeingTested = require('../../lib/thingBeingTested');
let customMatchers = require('../support/customMatchers');
describe('myTests', function() {
beforeEach(function() {
jasmine.addMatchers(customMatchers);
let app = {
use: function() {}
};
spyOn(app, 'use');
thingBeingTested(app);
});
it('calls app.use with the correct function', function() {
expect(app.use.calls.count()).toBe(1);
expect(app.use.calls.argsFor(0)).toBeTheSameFunctionAs(someExternalFunction);
});
});
If you want to compare two objects but ignore their functions, you can use the methods _.isEqualWith together with _.isFunction from lodash as follows.
function ignoreFunctions(objValue, otherValue) {
if (_.isFunction(objValue) && _.isFunction(otherValue)) {
return true;
}
}
it('check object equality but ignore their functions', () => {
...
expect(_.isEqualWith(actualObject, expectedObject, ignoreFunctions)).toBeTrue();
});
I'd like to use the built-in JsTestDriver functionality and code coverage support of the WebStorm IDE. However, I use mocha instead of Jasmine.
How can I configure webstorm to recognize mocha, or use a mocha plugin?
I did find this piece of code to create a mocha jstestdriver adapater on the web, but not sure how and where to add it to webstorm...
/**
* Mocha JsTestDriver Adapter.
* #author jan#prachar.eu (Jan Prachar)
*/
(function(){
/**
* Our mocha setup
*/
var setup = mocha.setup;
var mochaOptions = {};
mocha.setup = function (opts) {
if ('string' === typeof opts) {
mochaOptions.ui = opts;
} else {
mochaOptions = opts;
}
setup.call(mocha, mochaOptions);
};
var getReporter = function (onTestDone, onComplete) {
var Base = mocha.reporters.Base;
var Reporter = function (runner) {
var self = this;
Base.call(this, runner);
this.onTestDone = onTestDone;
this.onComplete = onComplete;
this.reset = function () {
jstestdriver.console.log_ = [];
};
this.reset();
runner.on('start', function () {
});
runner.on('suite', function (suite) {
});
runner.on('suite end', function (suite) {
});
runner.on('test', function (test) {
self.reset();
});
runner.on('pending', function () {
});
runner.on('pass', function (test) {
self.onTestDone(new jstestdriver.TestResult(
test.parent.fullTitle(),
test.title,
'passed',
'',
'',
test.duration
));
});
runner.on('fail', function (test, err) {
var message = {
message: err.message,
name: '',
stack: err.stack
};
self.onTestDone(new jstestdriver.TestResult(
test.parent.fullTitle(),
test.title,
'failed',
jstestdriver.angular.toJson([message]),
'',
test.duration
));
});
runner.on('end', function () {
self.onComplete();
});
};
// Inherit from Base.prototype
Reporter.prototype.__proto__ = Base.prototype;
return Reporter;
};
var MOCHA_TYPE = 'mocha test case';
TestCase('Mocha Adapter Tests', null, MOCHA_TYPE);
jstestdriver.pluginRegistrar.register({
name: 'mocha',
getTestRunsConfigurationFor: function (testCaseInfos, expressions, testRunsConfiguration) {
for (var i = 0; i < testCaseInfos.length; i++) {
if (testCaseInfos[i].getType() === MOCHA_TYPE) {
testRunsConfiguration.push(new jstestdriver.TestRunConfiguration(testCaseInfos[i], []));
}
}
},
runTestConfiguration: function (config, onTestDone, onComplete) {
if (config.getTestCaseInfo().getType() !== MOCHA_TYPE) return false;
mochaOptions.reporter = getReporter(onTestDone, onComplete);
mocha.setup(mochaOptions);
mocha.run();
return true;
},
onTestsFinish: function () {
}
});
})();
Does this Mocha Adapter really work?
Were you able to test it from the command?
I would imagine that it would be configured similar to how the jasmineAdapter.js is configured below.
server: http://<localhost>:4224
load:
- tools/Jasmine/jasmine.js
- tools/Jasmine/jasmineAdapter.js
- lib/require.js
- src/*.js
test:
- specs/*.js