I have a Sample project with angular-datatables with CRUD operations. I looked at Project Scaffolding at grunt web, but I can't find anything about javascript templates that fit my needs...
I saw init.copyAndProcess that is something like I want, but I need few improves to this. First, I want templates with conditional sentences on my js file, like(pseudocode):
{if prop.selectAllButton}
//declare header render on Datatables in several lines of code{/if}
Also, there will be like 10 or 20 options, and it could grow with the number of columns or by creating virtual columns. So, I want the props object to be on a file, not to be prompts on cli.
The table is not rendered in angular way.
Thanks in advance.
Chi.
Now i see that my questions is a bit obvious to answer if I move out of grunt Scaffolding and use yeoman...
In yo file, I can read my configFile in the initializing phase, like:
initializing: function () {
this.pkg = require('../package.json');
this.dtconfig = require(this.options['config-file-location']);
},
Where 'config-file-location' is an argument of the cli.
Them, in the writing phase, I use dtconfig as parameter for copyTpl:
scripts: function () {
this.fs.copyTpl(
this.templatePath('main.js'),
this.destinationPath('app/scripts/main.js'),
{//Parameters
selectAllButton: this.dtconfig.selectAllButton
}
);
And them, in the templates/main.js file:
<% if (selectAllButton) { %>
//apply selectaAll header render for dt.
Related
We have an UI development library consisting of widgets built on top of Angular JS. Let's pick "header-widget" as an example - a header container built with Angular JS, but with specific functionality on top if it.
Say the HTML code of this widget will look like this:
<header class="my-header">
<div ng-if="configuration.hasTitle">
<h1 class="my-header-title-text">
<span class="my-header-title-text">This is a main title</span>
<span class="my-header-subtitle-text">This is a subtitle</span>
</h1>
</div>
</header>
I can create an unit/integration test using Karma i.e. at one point I will have something like this:
element = angular.element("<header config=\"config\"></header>");
$compile(element)(myScope);
myScope.$digest();
expect(element.find(".my-header-title-text").text().trim()).toBe("This is a main title");
and I can create an end to end test using Protractor i.e. at one point I will have something like this:
describe('Some end to end spec', function () {
it('should have the right title', function () {
let title = element(by.css(".my-header .my-header-title-text"));
expect(title.getText()).toMatch("This is a main title");
});
});
Suppose the developers change the header and the new class for the title becomes "my-header-MAIN-title-text". I would have to go to all places in unit/integration tests as well as in the end to end tests to fix that.
(For the sake of this example, the change is trivial and in theory can be encapsulated by having selectors in separate files, but in practice I speak about bigger changes in the DOM structure of these widgets, etc.)
I want to build a test library - like a test API - on top of the widgets. An encapsulation layer that would offer me things like header.getTitle() and header.getSubtitle() - that I can use in both unit/integration (Karma) as well as in the end to end world (Protractor). This way, if a change like the one mentioned above will occur, I have to go in one place, fix the implementation of the "header.getTitle" and none of the tests should care about.
To do so, I have created something like this (using TypeScript):
import $ from "jquery";
export class MyHeaderImpl implements MyHeadear {
private selector;
constructor(element) {
this.selector = $(element);
}
getTitle(): string {
return this.selector.find(".my-header-title-text").text().trim();
}
getSubtitle(): string {
return this.selector.find(".my-header-subtitle-text").text().trim();
}
}
It works fine for my unit/integration tests (Karma):
element = angular.element("<header config=\"config\"></header>");
$compile(element)(myScope);
myScope.$digest();
const header = ComponentFactory.createHeader(element);
expect(header.getTitle()).toBe("This is a main title");
However, it will not work on Protractor:
describe('Some end to end spec', function () {
it('should have the right title', function () {
let element = element(by.css(".my-header .my-header-title-text"));
const header = ComponentFactory.createHeader(element);
expect(title.getText()).toMatch("This is a main title");
});
});
It throws the following exception
Failed: jquery_1.default is not a function
I am using TypeScript in the test library, as well as with my Protractor tests.
I can replace the jQuery $ with Protractor's element in my test library, but then I would force the unit/integration tests to run against a browser, which will defeat their purpose of being fast.
Does anyone knows how can I create such a test API library that can be used both by Karma tests, as well as by Protractor tests?
One of the best descriptions I could find here on Stack Overflow, but it does not answer my question.
Have you tried to inject the library in karma and use it directly without importing. There are tons of karma adapters so for jquery I think. In protractor you could do the same in protractor config. The test API you described is called Page Object pattern BTW, thou I'm not sure if it's a good idea to mix both type of tests in single spec.
#factor5, here is my answer to your suggestion, as the comment section doesn't allow it.
First, let me summarize my question like this:
is there a way to write (TypeScript) a method that accepts both:
Angular element (https://docs.angularjs.org/api/ng/function/angular.element)
Protractor element (https://www.protractortest.org/#/api?view=ElementFinder)
and then looks inside given element, finds a div, and returns its text?
If I define this element argument as the Angular one, I cannot reuse it in the Protractor end to end project.
If I define this element argument as "import {element} from protractor", it will force my unit/integration tests to be actually end to end tests, going through the WebDriver.
Second, about Page Object pattern: I go one level below Page Object pattern. Let's take the comment text area, where I type my answer right now.
<div class="js-comment-text-input-container">
<textarea></textarea>
</div>
I might have a Page Object called QuestionPage,with a method typeComment(), or even have a separate component (PageObject) called CommentSection, where I place methods like typeComment(), sendComment(), editComment(), etc.
But the might be used in many other places, i.e. in a page where user can
can add a short bio about, etc. There will be a Page Object called UserProfile, which has a typeBio() that inside also deals with
.
At this point, if the structure of changes, you already have two places where you have to
go and change.
Therefore, I need a separate library to isolate the interaction with such elements.
Ok, I'm near to the finish line with my new PHP/JS app built with Gulp and Browserify. The last part is how to "boot", I mean how to do the "first call".
Let's say I have 3 JS entry points
/js/articles.js
/js/categories.js
/js/comments.js
each of them using some JS modules.
Then I have 3 HTML files, requiring their JS
/articles.html
/categories.html
/comments.html
example /js/articles.js
var $ = require("jquery");
var common = require("../common.js");
var viewModel = {
readData: function() {
/* read record from API and render */
},
insert: function() {
/* open a modal to insert new record */
}
};
What I should do now is to perform this sort of "boot": that is calling some init function I need, then load server data, then bind all buttons and stuff to viewModel's methods
$(document).ready(function() {
common.init();
viewModel.readData();
$('#btn-add').click(viewModel.insert);
});
Ok, but where am I to put this?
A) In HTML file?
I can't cause I don't have any global JS variabile to access..
B) Am I put it into articles.js?
At the moment, my Gulp task will bundle everything (articles.js, categories.js, comments.js, common libraries) into a single bundle.js.
If I put it into articles.js it will end up into the bundle.js. So articles-related boot stuff would be called in "categories" page either. And this is wrong.
C) Should I split articles.js into 2 files, one containing viewModel definition and the other doing the $(document).ready stuff?... but again how do I access to the correct viewModel?
Which is the correct solution?
Thank you
Seems your gulp task would just concat all the entries into bundle.js so you probably could just add another entry called js/index.js and put your initialization code inside it.
It's confusing that your code will be executed (base on your description in B) even though you don't call require on it. Can you provide your bundle.js and one of your html file?
What is the right way to handle the Page Objects model for writing Protractor E2E testing for AngularJS? I feel like I should write them in separate files (like homepage.js, page2.js, etc) then include somehow them into the specs like modules as need. However, I don't know how to inject them. Would appreciate any suggestions. Thanks!
Keep them in separate files, and use Node's require to pull in any helpers or page objects you need. Check out this super simple example: https://github.com/juliemr/ng-page-e2e/blob/master/test/angularsite_test.js#L2
Have you tried with astrolabe? It's a PageObject implementation on top of Protractor that could guide your structure: https://github.com/stuplum/astrolabe
There you can see that the recommended structure is one page object per file.
E.g.: singInPage.js
var Page = require('astrolabe').Page;
module.exports = Page.create({
url: { value: 'http://<somesite>.com/signin' },
username: { get: function() { return this.findElement(this.by.input('username')); } }, // finds an input element with the name 'username'
submit: { get: function() { return this.findElement(this.by.id('submit')); } } // finds an element with the id 'submit'
});
it makes the writing of test cases and even page object very compact and readable.
You should keep them in separate files, yes.
And in your protractor referenceConf.js (config to launch protractor with) you should write:
specs: ['<your_path>/test/pages/*Test.js']
In this case< protractor will launch all files from dir "/test/pages" with mask *Test.js (loginPageTest.js, homePageTest.js)
I'm afraid that there's no common standards when it comes to testing with page objects.
You could find several proposals among protractor's issues: https://github.com/angular/protractor/issues/401 and https://github.com/angular/protractor/issues/78
For my needs I created very simple page objects in my open source project, see: https://github.com/9ci/angle-grinder/pull/124
Also quite interesting implementation you could find in https://github.com/juliemr/ng-page-e2e/tree/master/test
I am working with a website built with Jade/Express for a few weeks now. I recently organized the image folder for the website so all the images were disbursed between several folders to make it easier to use and sort through.
To make it easier to make changes to the hierarchy of images (and other such files) I wrote a script that contains some globals for file paths. Now I have been trying to get the script to run so that I can call functions inside the jade template to automatically use these globals.
For example. Images are now sorted into several folders:
File Hierarchy
img/
glyphs/
interactivity/
buttons/
...
In my path manager script, I created several functions, including the following:
In: path-manager.js
images_root_path = "/img/";
glyph_path = images_root_path + "glyphs/";
function getGlyph(color, name) {
return glyph_path + color + "/" + name;
}
I tried several methods to get the script to execute before the template. Here is one of the attempts:
In page.jade
include ../../../public/js/lib/path-manager.js
=NamespacePathManager();
The above is, in theory, supposed to include the js and then I execute the namespace below to make the functions available, but that isn't working.
This is a portion of the Jade template that I want to use the function in:
In page.jade after the script include
span.challenge-time
img(src=getGlyph("black","stopwatch.png"), style="margin-right:5px;")
The above example should return: "/img/glyphs/black/stopwatch.png"
The problem is, I believe, that the scripts I am trying to make available server-side to the jade template are not being executed before the jade template is rendered. Everything I have tried doing to get this to work always results in an error saying that the server doesn't recognize the function getGlyph or when I started using the namespace function, NamespacePathManager
Summary: I want a javascript file to execute before a jade template is rendered into a webpage so that I can call functions and variables from that javascript on the server to use while rendering the jade template. My problem is that all the methods I have tried are unable to execute the javascript before the Jade is rendered.
Update
One work around I found was to put the javascript into unbuffered code directly on the page including a jade. This isn't quite the elegant solution I was looking for, but it works for now
- some code
- more code
This code is executed inline. The downside is that I have to include it on every page manually - instead of just including it once and having the functions available everywhere.
You can register helper methods in Express that will then be accessible in the views.
So in your case, the path-manager.js can be the helper file that you register, and contains:
var images_root_path = "/img/";
var glyph_path = images_root_path + "glyphs/";
exports.helpers = {
getGlyph: function (color, name) {
return glyph_path + color + "/" + name;
}
// Other helper methods here..
};
Then when setting up the express server, you register the helper
var app = express.createServer();
// app.configure here...
// app.use ...
app.helpers(require('./path-manager.js').helpers);
// Routes set up here ..
Finally, you can call the helper method from Jade view like this:
span.challenge-time
img(src='#{getGlyph("black","stopwatch.png")}', style='margin-right:5px;')
There's a good write up on this topic at DailyJS http://dailyjs.com/2011/01/03/node-tutorial-8/
I will explain my idea behind this:
I use python for google app engine + js + css
the main project will be stored under the src folder like this:
\src
\app <--- here goes all the python app for gae
\javascript <--- my non-packed javascript files
\static_files <--- static files for gae
now the javascript dir looks like this
\javascript
\frameworks <--- maybe jQuery && jQueryUI
\models <--- js files
\controllers <--- js files
\views <--- HTML files!
app.js <--- the main app for js
compile.py <--- this is the file I will talk more
About compile.py:
This file will have 2 methods one for the min and other for the development javascript file;
When is run will do:
Join all the files with "js" extension;
The app.js contains a variable named "views" and is an object, like a hash; Then the compiler copy the contents of each file with "html" extension located in the "/javascript/views/" dir using this rule;
example: if we have a view like this "/views/login.html" then the "views" js var will have a property named "login"; views['login'] = '...content...';
example2: "/views/admin/sexyBitcy.html" then view['admin.sexyBitcy'] = '...content...' or whatever exists in that html file..;
Then this big file will be saved into the "/src/static_files/core.js"; if is minified will be saved as "/src/static_files/core.min.js";
The javascript will use dependenccy injection, or sort of it. (:
I will explain how it will work then:
the index.html that is loaded when you come into the site loads the core.js and the jquery.js;
the core.js will create the layout of the page, as SEO is not important for the most of the pages;
the core.js uses the controllers-models-views to create the layout of course; the html for the layout is inside the var "views"; will be a heavy variable of course!
Some code:
mvcInjector = new MVCInjector;
mvcInjector.mapView(views['login'], 'login', LoginController);
parent = $('#jscontent');
jquery
view = mvcInjector.instanceView('login', parent); // <--- this will create the contents of the views['login'] in the parent node "parent = $('#jscontent');" then will instance the LoginController that will map the "SkinParts" (like in FLEX if you know); what does it mean map the "SkinParts"? - when the user will click on a button an handler for that action is defined in the controller; ex.:
// LoginController
this.init = function(){
// map skin parts
this.mapSkinPart('email', 'input[name]="email"');
this.mapSkinPart('submit', 'input[name]="submit"');
// link skin parts to handlers
this.getSkinPart('submit').click = this.login;
}
// handlers
this.login = function(event){
// connect to the db
// some problems here the get the value as the "this" keyword references to the this of the controller class, I will work it around soon
alert('open window button1' + this.getSkinPart('email').value());
}
If something is not clear just say something, I will be happy to explain;
So the question remains: is this scalable, manageable and fast enough for a big RIA application build with javascript+jquery and maybe with jqueryUI?
Thanks ;)
I like your idea quit a bit.
I would think about loading html pages by ajax, if they are big and there are many of them...
Have a look on angular project, I hope, it could help you a lot. It's a kind of JS framework, designed to work together with jQuery. Well suitable for test driven development.
It uses html as templates, you can simply create your own controllers, use dependency injector, etc... Feel free to ask any question on mailing list.
Then, I must recommend JsTestDriver - really cool test runner for JS (so you can easily run unit tests in many browsers, during development - let's say after save...)