Difference between revisions of "User:Roger Pearse/JavaScript Basics"
Roger Pearse (talk | contribs) (→Mocha) |
Roger Pearse (talk | contribs) |
||
(57 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | = Open command window on directory in windows explorer = | ||
+ | |||
+ | Hold down shift and right-click folder, extra menu option. | ||
+ | |||
+ | = Load without saying http = | ||
+ | |||
+ | If you omit http, then the script will load correctly whether http or https. Otherwise it may give errors. | ||
+ | |||
+ | <pre> | ||
+ | <script src="//ajax.googleapis.com/ajax/libs/dojo/1.6.3/dojo/dojo.xd.js"></script> | ||
+ | </pre> | ||
+ | |||
+ | = Chrome tips = | ||
+ | |||
+ | Right-click on the reload, you get the menu - choose "empty cache and hard reload". (This menu only appears when developer tools are open) | ||
+ | |||
+ | [[File:Chrome-right-click-on-refresh.png]] | ||
+ | |||
+ | To inspect by hovering, click the left hand box+arrow. The elements tab will move to the item. The Network tab is worth having open on load, because it will list the .js files being loaded. | ||
+ | |||
+ | [[File:Chrome-inspect-hover.png]] | ||
+ | |||
+ | To find the JavaScript source, in order to set breakpoints, open the "other domain" and go down through files in order. | ||
+ | |||
+ | [[File:Devtools-source-javascript.png]] | ||
+ | |||
= JavaScript Language = | = JavaScript Language = | ||
* [https://webapplog.com/es6/ Top 10 ES6 Features Every Busy JavaScript Developer Must Know] - ES6 = JavaScript 2015 | * [https://webapplog.com/es6/ Top 10 ES6 Features Every Busy JavaScript Developer Must Know] - ES6 = JavaScript 2015 | ||
* [https://en.m.wikipedia.org/wiki/TypeScript TypeScript] - Typed JavaScript with a "transpiler" (or translator) | * [https://en.m.wikipedia.org/wiki/TypeScript TypeScript] - Typed JavaScript with a "transpiler" (or translator) | ||
+ | * [https://javascript.info/ Modern JavaScript Tutorial] - looks very sound | ||
+ | |||
+ | Further reading: | ||
+ | * You Don’t Know JavaScript series | ||
+ | * Scotch.io tutorials | ||
+ | * Codecademy tutorials | ||
+ | * JavaScript weekly newsletter | ||
+ | |||
+ | === Single line functions using arrow operator === | ||
+ | |||
+ | * https://www.sitepoint.com/es6-arrow-functions-new-fat-concise-syntax-javascript/ | ||
+ | |||
+ | ====Example==== | ||
+ | |||
+ | <pre> | ||
+ | describe("test", function() { | ||
+ | |||
+ | it('test 1', () => alert(1)); | ||
+ | |||
+ | }); | ||
+ | </pre> | ||
+ | |||
+ | is equivalent to: | ||
+ | |||
+ | <pre> | ||
+ | describe("test", function() { | ||
+ | |||
+ | it('test 1', function() { | ||
+ | alert(1); | ||
+ | }); | ||
+ | |||
+ | }); | ||
+ | </pre> | ||
+ | |||
+ | ====More info==== | ||
+ | |||
+ | There’s one more syntax for creating functions – very simple and concise. It’s called “arrow functions”, because it looks like this: | ||
+ | |||
+ | <pre> | ||
+ | let func = (arg1, arg2, ...argN) => expression | ||
+ | </pre> | ||
+ | |||
+ | This creates a function ''func'' that has arguments ''arg1..argN'', evaluates the expression on the right side with their use and returns its result. | ||
+ | |||
+ | In other words, it’s roughly the same as: | ||
+ | |||
+ | <pre> | ||
+ | let func = function(arg1, arg2, ...argN) { | ||
+ | return expression; | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | But much shorter. Example: | ||
+ | |||
+ | Normal function: | ||
+ | |||
+ | <pre> | ||
+ | let sum = function(a, b) { | ||
+ | return a + b; | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | Arrow function: | ||
+ | |||
+ | <pre> | ||
+ | let sum = (a, b) => a + b; | ||
+ | |||
+ | alert( sum(1, 2) ); // 3 | ||
+ | </pre> | ||
+ | |||
+ | If we have only one argument, then parentheses can be omitted, making that even shorter: | ||
+ | |||
+ | <pre> | ||
+ | // same as | ||
+ | // let double = function(n) { return n*2 } | ||
+ | let double = n => n*2; | ||
+ | |||
+ | alert( double(3) ); // 6 | ||
+ | </pre> | ||
+ | |||
+ | If there are no arguments, we can put empty parentheses: | ||
+ | |||
+ | <pre> | ||
+ | let sayHi = () => alert("Hello!"); | ||
+ | |||
+ | sayHi(); | ||
+ | </pre> | ||
+ | |||
+ | === Truthy and Falsy === | ||
+ | |||
+ | A non-boolean value that counts as true is called "truthy," and a non-boolean value that counts as false is called "falsey." | ||
+ | |||
+ | * http://adripofjavascript.com/blog/drips/truthy-and-falsy-values-in-javascript.html | ||
+ | |||
+ | When we say that a value is "truthy" in JavaScript, we don't just mean that the value is true. Rather, what we mean is that the value coerces to true when evaluated in a boolean context. | ||
+ | |||
+ | Used for conciseness, to avoid saying "if (x === undefined)". | ||
+ | |||
+ | Most things are truthy; only six are falsey: false, null, undefined, NaN, 0, "". All the rest expressions are truthy. (Total crap concept) | ||
+ | |||
+ | === Callbacks, synchronous and asynchronous === | ||
+ | |||
+ | A mocha test: | ||
+ | |||
+ | <pre> | ||
+ | /** | ||
+ | * A series of tests to demonstrate async callbacks and potential problems in node.js and mocha | ||
+ | * | ||
+ | * To Run: | ||
+ | * cd test | ||
+ | * mocha callbackTest.js | ||
+ | */ | ||
+ | var assert = require('assert'); | ||
+ | |||
+ | // Test suite | ||
+ | describe('Testing Callbacks', function(){ | ||
+ | |||
+ | //-------------------------------------------------------- | ||
+ | |||
+ | // test | ||
+ | it('Synchronous callback', function(){ | ||
+ | |||
+ | // A function | ||
+ | function greeting(name) { | ||
+ | console.log('Hello ' + name); | ||
+ | } | ||
+ | |||
+ | // A function that takes a function as a parameter | ||
+ | function processUserInput(callback) { | ||
+ | var name = "fred"; | ||
+ | callback(name); | ||
+ | } | ||
+ | |||
+ | // Run the second function, passing in the first | ||
+ | processUserInput(greeting); | ||
+ | }); | ||
+ | //-------------------------------------------------------- | ||
+ | |||
+ | // test - but this does not work... the timeout is cancelled by the end of the test, | ||
+ | // so the second and third never run. It works fine in browser tho | ||
+ | // https://houssein.me/javascript/2016/05/10/asynchronous-javascript-callbacks.html | ||
+ | it('Asynchronous callback which fails', function(){ | ||
+ | |||
+ | function functionFirst(callback) { | ||
+ | setTimeout(function() { | ||
+ | console.log('Second action'); | ||
+ | callback(); | ||
+ | }, 3000); | ||
+ | } | ||
+ | |||
+ | function functionSecond() { | ||
+ | console.log('Third action'); | ||
+ | } | ||
+ | |||
+ | functionFirst(function(){ | ||
+ | functionSecond(); | ||
+ | }); | ||
+ | |||
+ | console.log('First action'); | ||
+ | |||
+ | }); | ||
+ | |||
+ | //-------------------------------------------------------- | ||
+ | |||
+ | // Here's how to fix the previous test - use done() with mocha. | ||
+ | // https://medium.com/caffeine-and-testing/async-testing-with-mocha-with-callbacks-and-promises-5d0002661b3f | ||
+ | // However if timeout is greater than 2000, it fails. | ||
+ | // " Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves." | ||
+ | it('Asynchronous callback with done', function(done){ | ||
+ | |||
+ | function functionFirst(callback) { | ||
+ | setTimeout(function() { | ||
+ | console.log('Second action'); | ||
+ | callback(); | ||
+ | // Mocha tool to wait until done | ||
+ | done(); // <--- This was the bit I added to wait | ||
+ | }, 1500); | ||
+ | |||
+ | } | ||
+ | |||
+ | function functionSecond() { | ||
+ | console.log('Third action'); | ||
+ | } | ||
+ | |||
+ | functionFirst(function(){ | ||
+ | functionSecond(); | ||
+ | }); | ||
+ | |||
+ | console.log('First action'); | ||
+ | |||
+ | }); | ||
+ | |||
+ | //-------------------------------------------------------- | ||
+ | |||
+ | // Here's how to fix the previous test - specify a timeout for mocha. | ||
+ | // Discussion of this: | ||
+ | // https://stackoverflow.com/questions/16607039/in-mocha-testing-while-calling-asynchronous-function-how-to-avoid-the-timeout-er | ||
+ | // You can either set the timeout when running your test: | ||
+ | // mocha --timeout 15000 | ||
+ | // Or you can set the timeout for each suite or each test programmatically: | ||
+ | // this.timeout(15000); // add under the describe() or the it() | ||
+ | it('Asynchronous callback with done and timeout', function(done){ | ||
+ | |||
+ | this.timeout(15000); // add under the describe() or the it() | ||
+ | |||
+ | function functionFirst(callback) { | ||
+ | setTimeout(function() { | ||
+ | console.log('Second action'); | ||
+ | callback(); | ||
+ | // Mocha tool to wait until done | ||
+ | done(); // <--- This was the bit I added to wait | ||
+ | }, 3000); | ||
+ | |||
+ | } | ||
+ | |||
+ | function functionSecond() { | ||
+ | console.log('Third action'); | ||
+ | } | ||
+ | |||
+ | functionFirst(function(){ | ||
+ | functionSecond(); | ||
+ | }); | ||
+ | |||
+ | console.log('First action'); | ||
+ | |||
+ | }); | ||
+ | |||
+ | //-------------------------------------------------------- | ||
+ | |||
+ | it('Doms test', function(done){ | ||
+ | |||
+ | this.timeout(15000); // add under the describe() or the it() | ||
+ | |||
+ | // Define a function and assign to a variable "callbackExample", which takes another function and executes it after a delay | ||
+ | // Set the delay to 5 seconds | ||
+ | var callbackExample = function(callback) { | ||
+ | setTimeout(callback, 5000); | ||
+ | }; | ||
+ | |||
+ | // Call the function "callbackExample", passing in an anonymous function that writes "done" | ||
+ | // This will be executed after 5 seconds. | ||
+ | callbackExample(function() { | ||
+ | console.log("done by Dom"); | ||
+ | done(); // added by me at the end of the long-running bit | ||
+ | }); | ||
+ | |||
+ | }); | ||
+ | |||
+ | }) | ||
+ | </pre> | ||
+ | |||
+ | * [[File:NoddyCallbacks.zip]] - sample project | ||
+ | |||
+ | === Closures === | ||
+ | |||
+ | '''Name two uses for closures'''. | ||
+ | |||
+ | * Factory functions - create numbers of related functions. | ||
+ | * Namespacing private functions - keeping them limited in scope. | ||
+ | |||
+ | Beware - long lived closures can cause memory consumption. | ||
+ | |||
+ | '''What is a Closure?''' - a stateful function | ||
+ | |||
+ | A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time. | ||
+ | |||
+ | To use a closure, simply define a function inside another function and expose it. To expose a function, return it or pass it to another function. | ||
+ | |||
+ | The inner function will have access to the variables in the outer function scope, even after the outer function has returned. | ||
+ | |||
+ | '''Using Closures (Examples)''' | ||
+ | |||
+ | Among other things, closures are commonly used to give objects data privacy. Data privacy is an essential property that helps us program to an interface, not an implementation. In JavaScript, closures are the primary mechanism used to enable data privacy. When you use closures for data privacy, the enclosed variables are only in scope within the containing (outer) function. You can’t get at the data from an outside scope except through the object’s privileged methods. | ||
+ | |||
+ | * https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36 | ||
+ | * https://www.w3schools.com/js/js_function_closures.asp | ||
+ | * https://medium.com/written-in-code/practical-uses-for-closures-c65640ae7304 | ||
+ | |||
+ | <pre> | ||
+ | var greetingFactory = function(greeting) { // <-- self-invoking function; runs only once | ||
+ | |||
+ | // Set any private variables - once | ||
+ | |||
+ | return function(person) { // <-- the function expression | ||
+ | return greeting + “ “ + person; | ||
+ | }; | ||
+ | }; | ||
+ | |||
+ | // Run the self-invoking function, and return the function expression inner-function | ||
+ | var closure = greetingFactory(“Hi”); | ||
+ | |||
+ | // run the inner function | ||
+ | closure(“Louis”); // Hi Louis | ||
+ | </pre> | ||
+ | |||
+ | * The variable ''greetingFactory'' is assigned the return value of a self-invoking function. | ||
+ | |||
+ | * The self-invoking function only runs once. It sets any variables, and returns a function expression. | ||
+ | |||
+ | [[File:Object-prototype-empty.png]] | ||
+ | |||
+ | That [[Prototype]] has a “magical” meaning. When we want to read a property from object, and it’s missing, JavaScript automatically takes it from the prototype. In programming, such thing is called “prototypal inheritance”. | ||
+ | |||
+ | The property [[Prototype]] is internal and hidden, but there are many ways to set it. One of them is to use __proto__, like this: | ||
+ | |||
+ | <pre> | ||
+ | let animal = { | ||
+ | eats: true | ||
+ | }; | ||
+ | let rabbit = { | ||
+ | jumps: true | ||
+ | }; | ||
+ | |||
+ | rabbit.__proto__ = animal; | ||
+ | </pre> | ||
+ | |||
+ | If we look for a property in rabbit, and it’s missing, JavaScript automatically takes it from animal. | ||
+ | |||
+ | <pre> | ||
+ | let animal = { | ||
+ | eats: true | ||
+ | }; | ||
+ | let rabbit = { | ||
+ | jumps: true | ||
+ | }; | ||
+ | |||
+ | rabbit.__proto__ = animal; // (*) | ||
+ | |||
+ | // we can find both properties in rabbit now: | ||
+ | alert( rabbit.eats ); // true (**) | ||
+ | alert( rabbit.jumps ); // true | ||
+ | </pre> | ||
+ | |||
+ | Here the line (*) sets animal to be a prototype of rabbit. | ||
+ | |||
+ | Then, when alert tries to read property rabbit.eats (**), it’s not in rabbit, so JavaScript follows the [[Prototype]] reference and finds it in animal (look from the bottom up): | ||
+ | |||
+ | === Prototypal Inheritance === | ||
+ | |||
+ | This involves reuse without defining classes. | ||
+ | |||
+ | * https://javascript.info/prototype-inheritance | ||
+ | |||
+ | In JavaScript, objects have a special hidden property [[Prototype]]. This is null by default, but can be set to point to another object. That object is called “a prototype”. | ||
= Installing stuff with NPM = | = Installing stuff with NPM = | ||
Line 67: | Line 437: | ||
Webstorm is in first place, but VS Code is next, and free. | Webstorm is in first place, but VS Code is next, and free. | ||
+ | |||
+ | NOTE: "clear" works in the IDE | ||
== Coverage in editor == | == Coverage in editor == | ||
Line 79: | Line 451: | ||
NodeJs is a way to run JavaScript to create server-side json-based webservices very lightly. The MEAN stack - NodeJs and Mongo - is intended to replace the LAMP stack with PHP and Postgres, and scales and runs far faster. | NodeJs is a way to run JavaScript to create server-side json-based webservices very lightly. The MEAN stack - NodeJs and Mongo - is intended to replace the LAMP stack with PHP and Postgres, and scales and runs far faster. | ||
+ | |||
+ | It embeds the Google "V8" JavaScript Engine, which is open source. | ||
* [https://www.w3schools.com/nodejs/nodejs_get_started.asp W3 Schools tutorial] | * [https://www.w3schools.com/nodejs/nodejs_get_started.asp W3 Schools tutorial] | ||
Line 194: | Line 568: | ||
== Mocha == | == Mocha == | ||
− | Mocha seems to have the lead... | + | Mocha seems to have the lead among testing frameworks. It runs either in the browser, or in Node. It uses the js engine of either, depending on where it runs. |
* http://www.marcusoft.net/2014/02/mnb-mocha.html - a bit rubbish | * http://www.marcusoft.net/2014/02/mnb-mocha.html - a bit rubbish | ||
+ | * https://webapplog.com/tdd/ - some useful info | ||
* https://semaphoreci.com/community/tutorials/getting-started-with-node-js-and-mocha - better on setup | * https://semaphoreci.com/community/tutorials/getting-started-with-node-js-and-mocha - better on setup | ||
+ | |||
+ | [[File:NoddyExpress.zip]] - my project, minus anything installed locally | ||
Create '''src''' and '''test''' directories. Then install testing framework and expectation library into the project. | Create '''src''' and '''test''' directories. Then install testing framework and expectation library into the project. | ||
Line 206: | Line 583: | ||
</pre> | </pre> | ||
− | Note that we are using the --save option to automatically save these dependencies in our package.json file. | + | Note that we are using the '''--save''' option to automatically save these dependencies in our package.json file. But probably it should be '''--save-dev'''. |
We will set up the test command inside the package.json file, in order to run our tests simply by executing npm test from the command line. | We will set up the test command inside the package.json file, in order to run our tests simply by executing npm test from the command line. | ||
Line 216: | Line 593: | ||
Note that we have also changed the standard reporter that Mocha uses, and set up the spec reporter that is more verbose, and thus more suitable for beginners. You can explore [http://mochajs.org/#reporters other reporters on Mocha's official website]. | Note that we have also changed the standard reporter that Mocha uses, and set up the spec reporter that is more verbose, and thus more suitable for beginners. You can explore [http://mochajs.org/#reporters other reporters on Mocha's official website]. | ||
+ | |||
+ | Update the test command in package.json to contain the above command. That file should now look like this: | ||
+ | <pre> | ||
+ | { | ||
+ | "name": "converter", | ||
+ | "version": "0.0.0", | ||
+ | "description": "", | ||
+ | "main": "index.js", | ||
+ | "scripts": { | ||
+ | "test": "./node_modules/.bin/mocha --reporter spec" | ||
+ | }, | ||
+ | "author": "", | ||
+ | "license": "ISC" | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | Run it using this command in the project root: | ||
+ | <pre> | ||
+ | npm test | ||
+ | </pre> | ||
+ | |||
+ | Chai styles: http://chaijs.com/guide/styles/ Avoid Should! | ||
+ | |||
+ | Our simple test in the test dir is | ||
+ | <pre> | ||
+ | // cd test | ||
+ | // mocha noddyTest.js | ||
+ | // Doesn't require anything else | ||
+ | var assert = require('assert'); | ||
+ | |||
+ | describe('String#split', function(){ | ||
+ | it('should return an array', function(){ | ||
+ | assert(Array.isArray('a,b,c'.split(','))); | ||
+ | }); | ||
+ | }) | ||
+ | </pre> | ||
+ | |||
+ | Two files. simple.js in src, just exports a string, no functions. | ||
+ | <pre> | ||
+ | var options = 'local'; | ||
+ | |||
+ | exports.options = options; | ||
+ | </pre> | ||
+ | |||
+ | simpleTest.js in test: | ||
+ | <pre> | ||
+ | // My first test | ||
+ | 'use strict' | ||
+ | |||
+ | // Import chai, the assert module | ||
+ | const assert = require("chai").assert; | ||
+ | |||
+ | // Import the code under test | ||
+ | const config = require("../src/simple"); | ||
+ | const options = config.options; | ||
+ | |||
+ | // The test suite | ||
+ | // Sometimes, developers might want to skip a test case/suite (describe.skip() or it.skip()) | ||
+ | // or make them exclusive (describe.only() or describe.only()). | ||
+ | // Exclusivity means that only that particular test runs (the opposite of skip). | ||
+ | describe("Testing import", function () { | ||
+ | |||
+ | // A test | ||
+ | it("loads options", function (done) { | ||
+ | |||
+ | console.log('---->' + options); | ||
+ | assert.equal(options, "local"); | ||
+ | |||
+ | done(); // tell mocha that we're done and it can process next test | ||
+ | }); | ||
+ | |||
+ | }); | ||
+ | </pre> | ||
+ | |||
+ | === before and after, beforeEach and afterEach === | ||
+ | |||
+ | <pre> | ||
+ | describe("test", function() { | ||
+ | |||
+ | before(() => alert("Testing started – before all tests")); | ||
+ | after(() => alert("Testing finished – after all tests")); | ||
+ | |||
+ | beforeEach(() => alert("Before a test – enter a test")); | ||
+ | afterEach(() => alert("After a test – exit a test")); | ||
+ | |||
+ | it('test 1', () => alert(1)); | ||
+ | it('test 2', () => alert(2)); | ||
+ | |||
+ | }); | ||
+ | </pre> | ||
== Jasmine is used for BDD == | == Jasmine is used for BDD == | ||
+ | |||
+ | But ... is browser based. Try Mocha instead. | ||
* Jasmine quick start – excellent - http://www.bradoncode.com/blog/2015/05/12/angularjs-testing-getting-started/ | * Jasmine quick start – excellent - http://www.bradoncode.com/blog/2015/05/12/angularjs-testing-getting-started/ | ||
Line 265: | Line 734: | ||
* Extracted from http://www.marcusoft.net/2014/02/mnb-supertest.html | * Extracted from http://www.marcusoft.net/2014/02/mnb-supertest.html | ||
+ | * More good stuff here https://www.codementor.io/knownasilya/testing-express-apis-with-supertest-du107mcv2 | ||
=== GET === | === GET === | ||
Line 344: | Line 814: | ||
</pre> | </pre> | ||
− | == Mocking == | + | == Mocking - use Sinon == |
Use Sinon instead of Mockito. There’s a before() and after() function. Stub the xhtmlrequest calls. | Use Sinon instead of Mockito. There’s a before() and after() function. Stub the xhtmlrequest calls. | ||
+ | |||
+ | == Full example of API tested with Mocha == | ||
+ | |||
+ | * [[JavaScript API with Mocha]] | ||
+ | |||
+ | = Coverage using Istanbul and Mocha = | ||
+ | |||
+ | * https://stackoverflow.com/questions/16633246/code-coverage-with-mocha | ||
+ | * https://istanbul.js.org/docs/tutorials/mocha/ | ||
+ | |||
+ | <pre> | ||
+ | npm install -g istanbul | ||
+ | istanbul cover node_modules/mocha/bin/_mocha -- -R spec | ||
+ | </pre> | ||
+ | |||
+ | (Use ''istanbul cover _mocha -- -R spec'' on other than windows) | ||
+ | |||
+ | The output is in ''coverage/lcov-report/index.html'' | ||
+ | |||
+ | == nyc - the Istanbul command line interface == | ||
+ | |||
+ | I had trouble with this. It just hanged. | ||
+ | |||
+ | <pre> | ||
+ | npm install --save-dev nyc | ||
+ | </pre> | ||
+ | |||
+ | And prefix mocha in your package.json: | ||
+ | <pre> | ||
+ | "test": "nyc ./node_modules/.bin/mocha --reporter spec" | ||
+ | </pre> | ||
+ | |||
+ | But I couldn't get it to work. | ||
+ | |||
+ | * https://github.com/istanbuljs/nyc | ||
= Database access = | = Database access = | ||
Line 470: | Line 975: | ||
npm install mongodb | npm install mongodb | ||
</pre> | </pre> | ||
+ | |||
+ | = What is Routing? = | ||
+ | |||
+ | * https://stackoverflow.com/questions/10075507/what-does-javascript-routing-buy-you | ||
+ | |||
+ | It's a way to go to new pages without reloading the whole webpage again. So much faster. | ||
+ | |||
+ | :If you want to navigate from one application state, e.g. /admin/users, to another, e.g. /admin/orders, you could use a normal link as you suggest. But then you're going to cause the browser to navigate from one HTML page to another. This is, obviously, the normal way that one navigates around the Web. But in a JavaScript application, this is pretty inefficient! | ||
+ | |||
+ | :A more efficient way is for the link to fire an event that the application monitors, and for the application to respond by changing the application's state--perhaps by removing or hiding the users view and replacing it with an orders view. | ||
+ | |||
+ | :This allows the application to maintain the dynamic object model that it's already burned time and used memory to create. It makes the user interface respond more speedily, and, if you're using URL history management via a hashtag or pushState, it allows the user to navigate around your app using the back and forward buttons of their browser, without reloading every asset on the page every time and wiping your application state. | ||
+ | |||
+ | :URL management also allows deep linking to some page in the application: on load, your app's router examines the route string that it receives, tokenizes it, and loads up the interface you've specified in your routing table. | ||
+ | |||
+ | = Local http server = | ||
+ | |||
+ | * https://www.npmjs.com/package/http-server | ||
+ | |||
+ | Installation via npm: | ||
+ | <pre> | ||
+ | npm install http-server -g | ||
+ | </pre> | ||
+ | |||
+ | This will install http-server globally so that it may be run from the command line. | ||
+ | |||
+ | <pre> | ||
+ | Usage: | ||
+ | http-server [path] [options] | ||
+ | </pre> | ||
+ | |||
+ | [path] defaults to ./public if the folder exists, and ./ otherwise. | ||
+ | |||
+ | Now you can visit http://localhost:8080 to view your server. Then browse to files. | ||
+ | |||
+ | <pre> | ||
+ | rpearse@MKNB740 MINGW64 /u/NoddyDojo16 | ||
+ | $ npm install http-server -g | ||
+ | |||
+ | C:\Users\rpearse\AppData\Roaming\npm\http-server -> C:\Users\rpearse\AppData\Roaming\npm\node_modules\http-server\bin\http-server | ||
+ | C:\Users\rpearse\AppData\Roaming\npm\hs -> C:\Users\rpearse\AppData\Roaming\npm\node_modules\http-server\bin\http-server | ||
+ | + http-server@0.10.0 | ||
+ | added 23 packages in 4.201s | ||
+ | |||
+ | rpearse@MKNB740 MINGW64 /u/NoddyDojo16 | ||
+ | $ | ||
+ | |||
+ | rpearse@MKNB740 MINGW64 /u/NoddyDojo16 | ||
+ | $ http-server | ||
+ | Starting up http-server, serving ./ | ||
+ | Available on: | ||
+ | http://172.19.37.72:8080 | ||
+ | http://127.0.0.1:8080 | ||
+ | Hit CTRL-C to stop the server | ||
+ | [Tue Jul 11 2017 10:00:15 GMT+0100 (GMT Summer Time)] "GET /" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko" | ||
+ | [Tue Jul 11 2017 10:00:16 GMT+0100 (GMT Summer Time)] "GET /favicon.ico" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko" | ||
+ | [Tue Jul 11 2017 10:00:16 GMT+0100 (GMT Summer Time)] "GET /favicon.ico" Error (404): "Not found" | ||
+ | [Tue Jul 11 2017 10:00:18 GMT+0100 (GMT Summer Time)] "GET /helloDojo.htm" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko" | ||
+ | </pre> | ||
+ | |||
+ | = Dojo = | ||
+ | |||
+ | You can carry on working with legacy Dojo so long as you specify async false. | ||
+ | |||
+ | * [[File:Dojo_stuff.zip]] | ||
+ | |||
+ | Here's a sample page: | ||
+ | |||
+ | <pre> | ||
+ | <!DOCTYPE html> | ||
+ | <html > | ||
+ | <head> | ||
+ | |||
+ | <style type="text/css"> | ||
+ | .example{ | ||
+ | margin: 8px; | ||
+ | text-align: center; | ||
+ | padding: 5px; | ||
+ | border: 2px solid black; | ||
+ | color: white; | ||
+ | background-color: blue; | ||
+ | width: 200px; | ||
+ | height: 200px; | ||
+ | } | ||
+ | </style> | ||
+ | |||
+ | <!-- Specify async=true for modern/AMD mode, false for legacy mode --> | ||
+ | <!-- If we get "Uncaught ReferenceError: dojo is not defined" look at this | ||
+ | https://stackoverflow.com/questions/14598719/dojo-tutorial-dojo-is-not-defined --> | ||
+ | <script>dojoConfig = {async: false, parseOnLoad: false}</script> | ||
+ | |||
+ | <!-- Include Dojo --> | ||
+ | <script src="//ajax.googleapis.com/ajax/libs/dojo/1.9.7/dojo/dojo.js"></script> | ||
+ | |||
+ | <script> | ||
+ | |||
+ | // dojo.ready(function(){ | ||
+ | // //alert("Dojo version " + dojo.version + " is loaded"); | ||
+ | // dojo.byId("greeting").innerHTML += ", from " + dojo.version; | ||
+ | // }); | ||
+ | |||
+ | function init() { | ||
+ | //alert("Dojo ready, version:" + dojo.version); | ||
+ | //dojo.byId("greeting").innerHTML += ", from " + dojo.version; | ||
+ | // More initialization here | ||
+ | calc(); | ||
+ | } | ||
+ | |||
+ | dojo.ready(init); | ||
+ | |||
+ | function calc(){ | ||
+ | |||
+ | var node = dojo.byId("example"); | ||
+ | var marginsize = dojo._getMarginSize(node); | ||
+ | var contentbox = dojo._getContentBox(node); | ||
+ | var position = dojo.position(node); | ||
+ | |||
+ | var height = dojo.style("example", "height"); | ||
+ | var padding = dojo.style("example", "padding"); | ||
+ | var margin = dojo.style("example", "margin"); | ||
+ | var border = dojo.style("example", "border"); | ||
+ | |||
+ | var info = "" | ||
+ | + "<br>css height = " + height | ||
+ | + "<br>css padding = " + padding | ||
+ | + "<br>css margin = " + margin | ||
+ | + "<br>css border = " + border | ||
+ | + "<br><br>node.scrollHeight = " + node.scrollHeight | ||
+ | + "<br>node.offsetHeight = " + node.offsetHeight | ||
+ | + "<br><br><table>" | ||
+ | + "<tr><td>contentbox.h = " + contentbox.h + " <td>" + " <td>" + JSON.stringify(contentbox) | ||
+ | + "<tr><td>position.h = " + position.h + " <td> Plus 2 lots of padding and border" + " <td>" + JSON.stringify(position) | ||
+ | + "<tr><td>marginsize.h = " + marginsize.h + " <td> Plus 2 lots of margin " + " <td>" + JSON.stringify(marginsize); | ||
+ | |||
+ | dojo.byId("output").innerHTML = info; //JSON.stringify(output); | ||
+ | } | ||
+ | </script> | ||
+ | </head> | ||
+ | <body> | ||
+ | |||
+ | <!-- The box on the screen, styled and sized as above --> | ||
+ | <div class="example" id="example">Some example node 200px high</div> | ||
+ | |||
+ | <!-- The output --> | ||
+ | <p><strong>Output:</strong></p> | ||
+ | |||
+ | <!-- Just use pre ... but I can't for uploading to this wiki page --> | ||
+ | <div id="output" style="display: block;unicode-bidi: embed;font-family: monospace;white-space: pre;"></div> | ||
+ | |||
+ | </body> | ||
+ | </html> | ||
+ | </pre> | ||
+ | |||
+ | = AngularJs / Angular = | ||
+ | |||
+ | * AngularJS is version 1. [http://Angularjs.org Website for v1] | ||
+ | * Angular = Angular 2, a rewrite and different thing. [https://angular.io/ Website for v2] | ||
+ | * Angular 4 is an update of Angular 2. | ||
+ | |||
+ | https://www.youtube.com/watch?v=tnXO-i7944M - YouTube. Dan Wahlin - AngularJS in 20ish Minutes - NG-Conf 2014. Source code is [https://github.com/DanWahlin/AngularIn20JavaScript here], but no good. My own project is here: [[File:NoddyAngularJs.zip]], edited with Visual Studio Code. Folder on desktop. | ||
+ | * [https://docs.angularjs.org/misc/started Getting Started] | ||
+ | * [https://docs.angularjs.org/guide/concepts AngularJS concepts] | ||
+ | |||
+ | The principle behind Angular is to get away from DOM manipulation. Do UI stuff declaratively, and business logic with code. | ||
+ | |||
+ | * [https://github.com/angular/angular-seed Angular seed project] - basic skeleton to get started |
Latest revision as of 13:58, 5 October 2017
Contents
- 1 Open command window on directory in windows explorer
- 2 Load without saying http
- 3 Chrome tips
- 4 JavaScript Language
- 5 Installing stuff with NPM
- 6 Editor
- 7 Node.js
- 8 Webservices using Express
- 9 Express Generator
- 10 Unit Testing
- 11 Coverage using Istanbul and Mocha
- 12 Database access
- 13 What is Routing?
- 14 Local http server
- 15 Dojo
- 16 AngularJs / Angular
Open command window on directory in windows explorer
Hold down shift and right-click folder, extra menu option.
Load without saying http
If you omit http, then the script will load correctly whether http or https. Otherwise it may give errors.
<script src="//ajax.googleapis.com/ajax/libs/dojo/1.6.3/dojo/dojo.xd.js"></script>
Chrome tips
Right-click on the reload, you get the menu - choose "empty cache and hard reload". (This menu only appears when developer tools are open)
To inspect by hovering, click the left hand box+arrow. The elements tab will move to the item. The Network tab is worth having open on load, because it will list the .js files being loaded.
To find the JavaScript source, in order to set breakpoints, open the "other domain" and go down through files in order.
JavaScript Language
- Top 10 ES6 Features Every Busy JavaScript Developer Must Know - ES6 = JavaScript 2015
- TypeScript - Typed JavaScript with a "transpiler" (or translator)
- Modern JavaScript Tutorial - looks very sound
Further reading:
- You Don’t Know JavaScript series
- Scotch.io tutorials
- Codecademy tutorials
- JavaScript weekly newsletter
Single line functions using arrow operator
Example
describe("test", function() { it('test 1', () => alert(1)); });
is equivalent to:
describe("test", function() { it('test 1', function() { alert(1); }); });
More info
There’s one more syntax for creating functions – very simple and concise. It’s called “arrow functions”, because it looks like this:
let func = (arg1, arg2, ...argN) => expression
This creates a function func that has arguments arg1..argN, evaluates the expression on the right side with their use and returns its result.
In other words, it’s roughly the same as:
let func = function(arg1, arg2, ...argN) { return expression; }
But much shorter. Example:
Normal function:
let sum = function(a, b) { return a + b; };
Arrow function:
let sum = (a, b) => a + b; alert( sum(1, 2) ); // 3
If we have only one argument, then parentheses can be omitted, making that even shorter:
// same as // let double = function(n) { return n*2 } let double = n => n*2; alert( double(3) ); // 6
If there are no arguments, we can put empty parentheses:
let sayHi = () => alert("Hello!"); sayHi();
Truthy and Falsy
A non-boolean value that counts as true is called "truthy," and a non-boolean value that counts as false is called "falsey."
When we say that a value is "truthy" in JavaScript, we don't just mean that the value is true. Rather, what we mean is that the value coerces to true when evaluated in a boolean context.
Used for conciseness, to avoid saying "if (x === undefined)".
Most things are truthy; only six are falsey: false, null, undefined, NaN, 0, "". All the rest expressions are truthy. (Total crap concept)
Callbacks, synchronous and asynchronous
A mocha test:
/** * A series of tests to demonstrate async callbacks and potential problems in node.js and mocha * * To Run: * cd test * mocha callbackTest.js */ var assert = require('assert'); // Test suite describe('Testing Callbacks', function(){ //-------------------------------------------------------- // test it('Synchronous callback', function(){ // A function function greeting(name) { console.log('Hello ' + name); } // A function that takes a function as a parameter function processUserInput(callback) { var name = "fred"; callback(name); } // Run the second function, passing in the first processUserInput(greeting); }); //-------------------------------------------------------- // test - but this does not work... the timeout is cancelled by the end of the test, // so the second and third never run. It works fine in browser tho // https://houssein.me/javascript/2016/05/10/asynchronous-javascript-callbacks.html it('Asynchronous callback which fails', function(){ function functionFirst(callback) { setTimeout(function() { console.log('Second action'); callback(); }, 3000); } function functionSecond() { console.log('Third action'); } functionFirst(function(){ functionSecond(); }); console.log('First action'); }); //-------------------------------------------------------- // Here's how to fix the previous test - use done() with mocha. // https://medium.com/caffeine-and-testing/async-testing-with-mocha-with-callbacks-and-promises-5d0002661b3f // However if timeout is greater than 2000, it fails. // " Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves." it('Asynchronous callback with done', function(done){ function functionFirst(callback) { setTimeout(function() { console.log('Second action'); callback(); // Mocha tool to wait until done done(); // <--- This was the bit I added to wait }, 1500); } function functionSecond() { console.log('Third action'); } functionFirst(function(){ functionSecond(); }); console.log('First action'); }); //-------------------------------------------------------- // Here's how to fix the previous test - specify a timeout for mocha. // Discussion of this: // https://stackoverflow.com/questions/16607039/in-mocha-testing-while-calling-asynchronous-function-how-to-avoid-the-timeout-er // You can either set the timeout when running your test: // mocha --timeout 15000 // Or you can set the timeout for each suite or each test programmatically: // this.timeout(15000); // add under the describe() or the it() it('Asynchronous callback with done and timeout', function(done){ this.timeout(15000); // add under the describe() or the it() function functionFirst(callback) { setTimeout(function() { console.log('Second action'); callback(); // Mocha tool to wait until done done(); // <--- This was the bit I added to wait }, 3000); } function functionSecond() { console.log('Third action'); } functionFirst(function(){ functionSecond(); }); console.log('First action'); }); //-------------------------------------------------------- it('Doms test', function(done){ this.timeout(15000); // add under the describe() or the it() // Define a function and assign to a variable "callbackExample", which takes another function and executes it after a delay // Set the delay to 5 seconds var callbackExample = function(callback) { setTimeout(callback, 5000); }; // Call the function "callbackExample", passing in an anonymous function that writes "done" // This will be executed after 5 seconds. callbackExample(function() { console.log("done by Dom"); done(); // added by me at the end of the long-running bit }); }); })
- File:NoddyCallbacks.zip - sample project
Closures
Name two uses for closures.
- Factory functions - create numbers of related functions.
- Namespacing private functions - keeping them limited in scope.
Beware - long lived closures can cause memory consumption.
What is a Closure? - a stateful function
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
To use a closure, simply define a function inside another function and expose it. To expose a function, return it or pass it to another function.
The inner function will have access to the variables in the outer function scope, even after the outer function has returned.
Using Closures (Examples)
Among other things, closures are commonly used to give objects data privacy. Data privacy is an essential property that helps us program to an interface, not an implementation. In JavaScript, closures are the primary mechanism used to enable data privacy. When you use closures for data privacy, the enclosed variables are only in scope within the containing (outer) function. You can’t get at the data from an outside scope except through the object’s privileged methods.
- https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36
- https://www.w3schools.com/js/js_function_closures.asp
- https://medium.com/written-in-code/practical-uses-for-closures-c65640ae7304
var greetingFactory = function(greeting) { // <-- self-invoking function; runs only once // Set any private variables - once return function(person) { // <-- the function expression return greeting + “ “ + person; }; }; // Run the self-invoking function, and return the function expression inner-function var closure = greetingFactory(“Hi”); // run the inner function closure(“Louis”); // Hi Louis
- The variable greetingFactory is assigned the return value of a self-invoking function.
- The self-invoking function only runs once. It sets any variables, and returns a function expression.
That Prototype has a “magical” meaning. When we want to read a property from object, and it’s missing, JavaScript automatically takes it from the prototype. In programming, such thing is called “prototypal inheritance”.
The property Prototype is internal and hidden, but there are many ways to set it. One of them is to use __proto__, like this:
let animal = { eats: true }; let rabbit = { jumps: true }; rabbit.__proto__ = animal;
If we look for a property in rabbit, and it’s missing, JavaScript automatically takes it from animal.
let animal = { eats: true }; let rabbit = { jumps: true }; rabbit.__proto__ = animal; // (*) // we can find both properties in rabbit now: alert( rabbit.eats ); // true (**) alert( rabbit.jumps ); // true
Here the line (*) sets animal to be a prototype of rabbit.
Then, when alert tries to read property rabbit.eats (**), it’s not in rabbit, so JavaScript follows the Prototype reference and finds it in animal (look from the bottom up):
Prototypal Inheritance
This involves reuse without defining classes.
In JavaScript, objects have a special hidden property Prototype. This is null by default, but can be set to point to another object. That object is called “a prototype”.
Installing stuff with NPM
Download and install nodejs. That gives you npm. This will end up in c:\program files.
Then you can install tools globally, or inside the project. Things like npm are global; things like Jasmine and Mocha are put inside the project.
Install into the project
Install tools into the project using:
npm install karma --save-dev
Rather than globally with
npm install karma -g
because then you aren’t dependent on local PC configuration. It's part of the project, in other words.
What’s –save-dev? That’s an option that inserts an entry pointing to the installed packages in the ‘devDependencies’ section of the package.json file. It signifies that a developer will need this package to work with the application, but it’s not required to run the application i.e. in production. So this is test-only stuff.
http://www.bradoncode.com/blog/2015/05/19/karma-angularjs-testing/
Starting a new node project
Start any node.js project by:
mkdir MyProject cd MyProject echo {} >> package.json
Starting the project 2
Npm's init command line option will launch a wizard, which creates a package.json for our project.
npm init
Do this after creating the directory.
Path problem in Windows
If you find that stuff installed with npm is not in the path in git bash, this means that when node was installed, the idiot didn't install as administrator, and the path stuff ended up in his local environment variables, rather than in the system environment variables. Might be able to fix via Windows | env, manually. Otherwise deinstall node from Control Panel, and reinstall.
Editor
Getting started with VS Code
- Get it from here: https://code.visualstudio.com/Download
- Great little tutorial! – add to basics - VS Code is the editor to use.
- Setting up a VS Code for typescript, mocha, etc - good on VS Code extensions
Dark IDE seems to be a point of pride for JavaScript IDE's - you're not a professional unless you are using that.
Comparison of IDE's (googled for node js ide) - useful! https://www.slant.co/topics/46/~best-ides-for-node-js
Webstorm is in first place, but VS Code is next, and free.
NOTE: "clear" works in the IDE
Coverage in editor
Webstorm includes code coverage in IDE, whereas VS Code has to use the paid for Wallaby.
Node.js
NodeJs is a way to run JavaScript to create server-side json-based webservices very lightly. The MEAN stack - NodeJs and Mongo - is intended to replace the LAMP stack with PHP and Postgres, and scales and runs far faster.
It embeds the Google "V8" JavaScript Engine, which is open source.
Noddy example
My first js (saved in myfirst.js):
'use strict' // Based on: // https://www.w3schools.com/nodejs/nodejs_get_started.asp // Revised against: // https://nodejs.org/api/synopsis.html // Get a handle on the http interfaces in node.js // originally had "var" - use const (for immutable) or let (for assignable) const http = require('http'); const hostname = '127.0.0.1'; const port = 8080; // Create a server and pass a function in to listen to the request. // The function passed in is a "request listener" which is automatically added to the "request" event // The createServer() returns a new instance of http.Server // In the horrible condensed way of js, the server is activated at the same time by .listen() on the end, // in the original example. But split out in the node.js docs, so have done the same const myServer = http.createServer(function (request, response) { response.writeHead(200, {'Content-Type': 'text/html'}); response.end('Hello World!'); }); myServer.listen(port, hostname); console.log("Not blocked by the listen");
Create a file, then in git bash (or in the VS code terminal) do
node myfirst.js
Then open Chrome and do http://localhost:8080, and see the hello world.
Modules available for nodejs
The above example uses the http module. Here's a list of all of them in node 6: https://www.w3schools.com/nodejs/ref_modules.asp
Colours in console
npm install chalk
and
const chalk = require('chalk'); console.log(chalk.blue('Hello world!'));
Webservices using Express
- http://www.marcusoft.net/2014/02/mnb-express.html
- What is the different between http module and express modle:
- Express is not a "module", it's a framework: it gives you an API, submodules, and methodology and conventions for quickly and easily tying together all the components necessary to put up a modern, functional web server with all the conveniences necessary for that (static asset hosting, templating, handling XSRF, CORS, cookie parsing, POST parsing, you name it, it probably lets you use it).
- The http API that's baked into Node.js, on the other hand, is just the http module: it can set up HTTP connections and send and receive data, as long as it uses the hypertext transfer protocol (with the relevant HTTP verb) and that's... well that's it really.
- the Express module is built on top of the http module. It uses the http module for managing the incoming http connections. But, it adds a ton of additional functionality on top of the http module. That is the point of it. For example, if you want to server whole directory of static files (like CSS files or script files) from your node server, that can be done with one line of code in node.js, but would take a lot more code with only the http module
Express Generator
MVC framework built on Node. Has an express generator that will build the project dirs. From here:
Install the Express Generator by running the following from a terminal:
npm install -g express-generator
The -g switch installs the Express Generator globally on your machine so you can run it from anywhere. You won't want it as part of your project anyway.
We can now scaffold a new Express application called myExpressApp by running:
express myExpressApp
This creates a new folder called myExpressApp with the contents of your application. To install all of the application's dependencies (again shipped as NPM modules), go to the new folder and execute npm install:
cd myExpressApp npm install
At this point, we should test that our application runs. The generated Express application has a package.json file which includes a start script to run node ./bin/www. This will start the Node.js application running.
From a terminal in the Express application folder, run:
npm start
The Node.js web server will start and you can browse to http://localhost:3000 to see the running application.
Unit Testing
Mocha
Mocha seems to have the lead among testing frameworks. It runs either in the browser, or in Node. It uses the js engine of either, depending on where it runs.
- http://www.marcusoft.net/2014/02/mnb-mocha.html - a bit rubbish
- https://webapplog.com/tdd/ - some useful info
- https://semaphoreci.com/community/tutorials/getting-started-with-node-js-and-mocha - better on setup
File:NoddyExpress.zip - my project, minus anything installed locally
Create src and test directories. Then install testing framework and expectation library into the project.
npm install mocha --save npm install chai --save
Note that we are using the --save option to automatically save these dependencies in our package.json file. But probably it should be --save-dev.
We will set up the test command inside the package.json file, in order to run our tests simply by executing npm test from the command line.
The following command is used to invoke the Mocha binary installed locally in the ./node_modules directory:
./node_modules/.bin/mocha --reporter spec
Note that we have also changed the standard reporter that Mocha uses, and set up the spec reporter that is more verbose, and thus more suitable for beginners. You can explore other reporters on Mocha's official website.
Update the test command in package.json to contain the above command. That file should now look like this:
{ "name": "converter", "version": "0.0.0", "description": "", "main": "index.js", "scripts": { "test": "./node_modules/.bin/mocha --reporter spec" }, "author": "", "license": "ISC" }
Run it using this command in the project root:
npm test
Chai styles: http://chaijs.com/guide/styles/ Avoid Should!
Our simple test in the test dir is
// cd test // mocha noddyTest.js // Doesn't require anything else var assert = require('assert'); describe('String#split', function(){ it('should return an array', function(){ assert(Array.isArray('a,b,c'.split(','))); }); })
Two files. simple.js in src, just exports a string, no functions.
var options = 'local'; exports.options = options;
simpleTest.js in test:
// My first test 'use strict' // Import chai, the assert module const assert = require("chai").assert; // Import the code under test const config = require("../src/simple"); const options = config.options; // The test suite // Sometimes, developers might want to skip a test case/suite (describe.skip() or it.skip()) // or make them exclusive (describe.only() or describe.only()). // Exclusivity means that only that particular test runs (the opposite of skip). describe("Testing import", function () { // A test it("loads options", function (done) { console.log('---->' + options); assert.equal(options, "local"); done(); // tell mocha that we're done and it can process next test }); });
before and after, beforeEach and afterEach
describe("test", function() { before(() => alert("Testing started – before all tests")); after(() => alert("Testing finished – after all tests")); beforeEach(() => alert("Before a test – enter a test")); afterEach(() => alert("After a test – exit a test")); it('test 1', () => alert(1)); it('test 2', () => alert(2)); });
Jasmine is used for BDD
But ... is browser based. Try Mocha instead.
- Jasmine quick start – excellent - http://www.bradoncode.com/blog/2015/05/12/angularjs-testing-getting-started/
My NoddyJasmine.htm, which you just open in Chrome, is:
<html> <head> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/jasmine.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/jasmine.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/jasmine-html.min.js"></script> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.3/boot.min.js"></script> </head> <body> </body> <script type="text/javascript"> // Paste in the test code here. // This example from // http://www.bradoncode.com/blog/2015/05/12/angularjs-testing-getting-started/ // The describe function represents a spec (logical grouping of tests). describe('calculator', function () { // The it function indicates a test within the logical grouping. // The first bit is the displayed test name, the function is the actual test it('1 + 1 should equal 2, others', function() { expect(1 + 1).toBe(2); // Other possible tests using Jasmine's expect() method expect(true).toBe(true); expect(false).not.toBe(true); expect(1).toEqual(1); expect('foo').toEqual('foo'); expect('foo').not.toEqual('bar'); }); }); </script> </html>
SuperTest for calling APIs
- Extracted from http://www.marcusoft.net/2014/02/mnb-supertest.html
- More good stuff here https://www.codementor.io/knownasilya/testing-express-apis-with-supertest-du107mcv2
GET
The webservice, in getApp.js:
var app = require("express")(); app.get('/user', function(req, res){ res.send(200, { name: 'marcus' }); }); // In order to reach the app from other modules // we need to export the express application module.exports.getApp = app;
The test in getAppTest.js:
var request = require('supertest'); // Here we get hold of the express application // by using the exported 'getApp'-property var app = require("./getApp").getApp; describe('GET /users', function(){ it('respond with json', function(done){ // the request-object is the supertest top level api request(app) .get('/user') .set('Accept', 'application/json') .expect('Content-Type', /json/) .expect(200, done); // note that we're passing the done as parameter to the expect }); });
POST
Post endpoint in postApp.js:
var express = require("express"); var app = express(); app.use(express.bodyParser()); app.post("/users", function(req, res){ var name = req.body.username; var email = req.body.email; // store it res.send(200, name + " is stored"); }); app.listen(3000);
The test - postAppTest.js:
var request = require('supertest'); describe("Posting is easy to test with supertest", function (){ it("posts a new user to /users", function(done){ var user = { username : 'marcus', email : 'marcus@marcus.com'}; request("http://localhost:3000") .post("/users") .send(user) .expect(200) .expect("marcus is stored", done); }); });
Mocking - use Sinon
Use Sinon instead of Mockito. There’s a before() and after() function. Stub the xhtmlrequest calls.
Full example of API tested with Mocha
Coverage using Istanbul and Mocha
- https://stackoverflow.com/questions/16633246/code-coverage-with-mocha
- https://istanbul.js.org/docs/tutorials/mocha/
npm install -g istanbul istanbul cover node_modules/mocha/bin/_mocha -- -R spec
(Use istanbul cover _mocha -- -R spec on other than windows)
The output is in coverage/lcov-report/index.html
nyc - the Istanbul command line interface
I had trouble with this. It just hanged.
npm install --save-dev nyc
And prefix mocha in your package.json:
"test": "nyc ./node_modules/.bin/mocha --reporter spec"
But I couldn't get it to work.
Database access
Connect to MySQL
Install the driver:
npm install -g mysql
Need:
// get the module for the driver var mysql = require('mysql'); // Create the connection var mysql = require('mysql'); var con = mysql.createConnection({ host: "localhost", user: "yourusername", password: "yourpassword" }); con.connect(function(err) { if (err) throw err; console.log("Connected!"); });
Run this file using:
C:\Users\Your Name>node demo_db_connection.js
Query the db with an SQL string:
con.connect(function(err) { if (err) throw err; console.log("Connected!"); con.query(sql, function (err, result) { if (err) throw err; console.log("Result: " + result); }); });
All rows: ( https://www.w3schools.com/nodejs/nodejs_mysql_select.asp )
var mysql = require('mysql'); var con = mysql.createConnection({ host: "localhost", user: "yourusername", password: "yourpassword", database: "mydb" }); con.connect(function(err) { if (err) throw err; con.query("SELECT * FROM customers", function (err, result, fields) { if (err) throw err; console.log(result); }); });
Connect to Postgres
Need the pg driver.
'use strict' const pg = require('pg') const conString = 'postgres://username:password@localhost/node_hero' // make sure to match your own database's credentials pg.connect(conString, function (err, client, done) { if (err) { return console.error('error fetching client from pool', err) } client.query('SELECT $1::varchar AS my_first_query', ['node hero'], function (err, result) { done() if (err) { return console.error('error happened during query', err) } console.log(result.rows[0]) process.exit(0) }) })
client.query can run any SQL, INSERT, etc.
Connect to Mongo
Most use Mongoose library.
Get a free MongoDB database at https://www.mongodb.com/
Get the driver:
npm install mongodb
What is Routing?
It's a way to go to new pages without reloading the whole webpage again. So much faster.
- If you want to navigate from one application state, e.g. /admin/users, to another, e.g. /admin/orders, you could use a normal link as you suggest. But then you're going to cause the browser to navigate from one HTML page to another. This is, obviously, the normal way that one navigates around the Web. But in a JavaScript application, this is pretty inefficient!
- A more efficient way is for the link to fire an event that the application monitors, and for the application to respond by changing the application's state--perhaps by removing or hiding the users view and replacing it with an orders view.
- This allows the application to maintain the dynamic object model that it's already burned time and used memory to create. It makes the user interface respond more speedily, and, if you're using URL history management via a hashtag or pushState, it allows the user to navigate around your app using the back and forward buttons of their browser, without reloading every asset on the page every time and wiping your application state.
- URL management also allows deep linking to some page in the application: on load, your app's router examines the route string that it receives, tokenizes it, and loads up the interface you've specified in your routing table.
Local http server
Installation via npm:
npm install http-server -g
This will install http-server globally so that it may be run from the command line.
Usage: http-server [path] [options]
[path] defaults to ./public if the folder exists, and ./ otherwise.
Now you can visit http://localhost:8080 to view your server. Then browse to files.
rpearse@MKNB740 MINGW64 /u/NoddyDojo16 $ npm install http-server -g C:\Users\rpearse\AppData\Roaming\npm\http-server -> C:\Users\rpearse\AppData\Roaming\npm\node_modules\http-server\bin\http-server C:\Users\rpearse\AppData\Roaming\npm\hs -> C:\Users\rpearse\AppData\Roaming\npm\node_modules\http-server\bin\http-server + http-server@0.10.0 added 23 packages in 4.201s rpearse@MKNB740 MINGW64 /u/NoddyDojo16 $ rpearse@MKNB740 MINGW64 /u/NoddyDojo16 $ http-server Starting up http-server, serving ./ Available on: http://172.19.37.72:8080 http://127.0.0.1:8080 Hit CTRL-C to stop the server [Tue Jul 11 2017 10:00:15 GMT+0100 (GMT Summer Time)] "GET /" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko" [Tue Jul 11 2017 10:00:16 GMT+0100 (GMT Summer Time)] "GET /favicon.ico" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko" [Tue Jul 11 2017 10:00:16 GMT+0100 (GMT Summer Time)] "GET /favicon.ico" Error (404): "Not found" [Tue Jul 11 2017 10:00:18 GMT+0100 (GMT Summer Time)] "GET /helloDojo.htm" "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; rv:11.0) like Gecko"
Dojo
You can carry on working with legacy Dojo so long as you specify async false.
Here's a sample page:
<!DOCTYPE html> <html > <head> <style type="text/css"> .example{ margin: 8px; text-align: center; padding: 5px; border: 2px solid black; color: white; background-color: blue; width: 200px; height: 200px; } </style> <!-- Specify async=true for modern/AMD mode, false for legacy mode --> <!-- If we get "Uncaught ReferenceError: dojo is not defined" look at this https://stackoverflow.com/questions/14598719/dojo-tutorial-dojo-is-not-defined --> <script>dojoConfig = {async: false, parseOnLoad: false}</script> <!-- Include Dojo --> <script src="//ajax.googleapis.com/ajax/libs/dojo/1.9.7/dojo/dojo.js"></script> <script> // dojo.ready(function(){ // //alert("Dojo version " + dojo.version + " is loaded"); // dojo.byId("greeting").innerHTML += ", from " + dojo.version; // }); function init() { //alert("Dojo ready, version:" + dojo.version); //dojo.byId("greeting").innerHTML += ", from " + dojo.version; // More initialization here calc(); } dojo.ready(init); function calc(){ var node = dojo.byId("example"); var marginsize = dojo._getMarginSize(node); var contentbox = dojo._getContentBox(node); var position = dojo.position(node); var height = dojo.style("example", "height"); var padding = dojo.style("example", "padding"); var margin = dojo.style("example", "margin"); var border = dojo.style("example", "border"); var info = "" + "<br>css height = " + height + "<br>css padding = " + padding + "<br>css margin = " + margin + "<br>css border = " + border + "<br><br>node.scrollHeight = " + node.scrollHeight + "<br>node.offsetHeight = " + node.offsetHeight + "<br><br><table>" + "<tr><td>contentbox.h = " + contentbox.h + " <td>" + " <td>" + JSON.stringify(contentbox) + "<tr><td>position.h = " + position.h + " <td> Plus 2 lots of padding and border" + " <td>" + JSON.stringify(position) + "<tr><td>marginsize.h = " + marginsize.h + " <td> Plus 2 lots of margin " + " <td>" + JSON.stringify(marginsize); dojo.byId("output").innerHTML = info; //JSON.stringify(output); } </script> </head> <body> <!-- The box on the screen, styled and sized as above --> <div class="example" id="example">Some example node 200px high</div> <!-- The output --> <p><strong>Output:</strong></p> <!-- Just use pre ... but I can't for uploading to this wiki page --> <div id="output" style="display: block;unicode-bidi: embed;font-family: monospace;white-space: pre;"></div> </body> </html>
AngularJs / Angular
- AngularJS is version 1. Website for v1
- Angular = Angular 2, a rewrite and different thing. Website for v2
- Angular 4 is an update of Angular 2.
https://www.youtube.com/watch?v=tnXO-i7944M - YouTube. Dan Wahlin - AngularJS in 20ish Minutes - NG-Conf 2014. Source code is here, but no good. My own project is here: File:NoddyAngularJs.zip, edited with Visual Studio Code. Folder on desktop.
The principle behind Angular is to get away from DOM manipulation. Do UI stuff declaratively, and business logic with code.
- Angular seed project - basic skeleton to get started