Subsecciones


Práctica: Conversor de Temperaturas

Donde

Véase https://bitbucket.org/casiano/pl-grado-temperature-converter/src. Este repo en bitbucket es privado del profesor. El de GitHub es público pero no está completo.
~/local/src/javascript/PLgrado/temperature(master)]$ git remote -v
github  git@github.com:crguezl/ull-etsii-grado-pl-1213-temperature-converter.git (fetch)
github  git@github.com:crguezl/ull-etsii-grado-pl-1213-temperature-converter.git (push)
origin  ssh://git@bitbucket.org/casiano/pl-grado-temperature-converter.git (fetch)
origin  ssh://git@bitbucket.org/casiano/pl-grado-temperature-converter.git (push)

Hay varias ramas (2015):

[~/local/src/javascript/PLgrado/temperature(master)]$ git branch -a
  gh-pages
  html5pattern
  karma
* master
  remotes/github/gh-pages
  remotes/github/master
  remotes/origin/html5pattern
  remotes/origin/karma
  remotes/origin/master
[~/local/src/javascript/PLgrado/temperature(master)]$

En mi portátil (29/09/2015) un clon del repo se encuentra en:

[~/srcPLgrado/temperature(master)]$ pwd -P
/Users/casiano/local/src/javascript/PLgrado/temperature # 27/01/2014

index.html

<html>
  <head>
     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     <title>JavaScript Temperature Converter</title>
     <link href=normalize.css" rel="stylesheet" type="text/css">
     <link href="global.css" rel="stylesheet" type="text/css">

     <script type="text/javascript" src="temperature.js"></script>
  </head>
  <body>
    <h1>Temperature Converter</h1>
    <table>
      <tr>
        <th>Enter  Temperature (examples: 32F, 45C, -2.5f):</th>
        <td><input id="original" autofocus onchange="calculate();" placeholder="32F" size="50"></td>
      </tr>
      <tr>
        <th>Converted Temperature:</th>
        <td><span class="output" id="converted"></span></td>
      </tr>
    </table>
  </body>
</html>

Instale Emmet

Escribir HTML es farragoso. Una solución es usar algún plugin para su editor favorito.

Emmet existe para diversos editores, entre ellos para

input tag

onchange

The onchange event occurs when the value of an element has been changed.

link tag

CSS

CSS stands for Cascading Style Sheets and is a separate, but complementary, language to HTML. CSS is what we use to apply styles to the content on our web page.

global.css

[~/srcPLgrado/temperature(master)]$ cat global.css 
th, td      { vertical-align: top; text-align: right; font-size:large; }     /* Don't center table cells  */
#converted  { color: red; font-weight: bold; font-size:large;          }     /* Calculated values in bold */
input       { 
              text-align: right;       /* Align input to the right  */
              border: none; 
              border-radius: 20px 20px 20px 20px;
              padding: 5px  5px;
              font-size:large;       }
body
{
 background-color:#b0c4de;  /* blue */
 font-size:large;
 font-family: "Lucida Sans Typewriter", "Lucida Console", Monaco, "Bitstream Vera Sans Mono", monospace;
}

h1 {
    font-weight: normal;
    font-family: "Brush Script MT", cursive;
    background: #3C5681;
    padding: 5px 15px;
    color: white;
    display:inline-block;
    border-radius: 10px 10px 10px 10px;
}

Sintáxis CSS

th, td      { vertical-align: top; text-align: right; font-size:large; }     /* Don't center table cells  */
What you see above is referred to as a rule set.

Introducción a los Selectores CSS

Ejercicio 1.2.1   Usa jsfiddle.net para encontrar las respuestas a las preguntas.

Ejercicio 1.2.2  

CSS reset

Every browser applies certain styles to elements on a web page by default.

For example, if you use an un-ordered list (the <ul> element) the browser will display the list with some existing formatting styles, including bullets next to the individual list items (the <li> elements inside the <ul>).

By using a CSS reset document at the top of your CSS file, you can reset all these styles to a bare minimum.

Two of the most popular CSS resets are Eric Meyer's Reset and Nicolas Gallagher's Normalize.css

    <title>JavaScript Temperature Converter</title>
    <link href=normalize.css" rel="stylesheet" type="text/css">
    <link href="global.css" rel="stylesheet" type="text/css">

El Modelo de Caja

The box model refers to the usually invisible rectangular area that is created for each HTML element. This area has four basic components

Ejercicio 1.2.3  

Editing CSS styles in Chrome using various DevTools aid

Depurar estilos puede ser complicado. Lea el artículo Tips for Debugging HTML and CSS.

While you can not "debug" CSS, because it is not a scripting language, you can utilize the Chrome DevTools Elements panel to inspect an element and view the Styles pane on the right.

This will give you insights as to the styles being overridden or ignored (line threw).

The Styles pane is also useful because of it's ability to LiveEdit the document being inspected, which may help you iron out the issues.

If the styles are being overridden, you can then view the Computed Style pane to see the CSS that is actually being utilized to style your document.

Block versus Inline

HTML elements fall under two categories: block or inline.

Propiedades CSS

temperature.js

"use strict"; // Use ECMAScript 5 strict mode in browsers that support it
function calculate() {
  var result;
  var original       = document.getElementById("........");
  var temp = original.value;
  var regexp = /.............................../;
  
  var m = temp.match(......);
  
  if (m) {
    var num = ....;
    var type = ....;
    num = parseFloat(num);
    if (type == 'c' || type == 'C') {
      result = (num * 9/5)+32;
      result = ..............................
    }
    else {
      result = (num - 32)*5/9;
      result = ............................
    }
    converted.innerHTML = result;
  }
  else {
    converted.innerHTML = "ERROR! Try something like '-4.2C' instead";
  }
}
    

Despliegue

Creando un fichero package.json

Para saber todo sobre ipackage.json visite este manual de npm o bien escriba npm help json en la línea de comandos.

The command:

npm init [-f|--force|-y|--yes]

Will ask you a bunch of questions, and then write a package.json for you.

If you already have a package.json file, it'll read that first, and default to the options in there.

It is strictly additive, so it does not delete options from your package.json without a really good reason to do so.

If you invoke it with -f, --force, it will use only defaults and not prompt you for any options.

[/tmp/pl-grado-temperature-converter(karma)]$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (pl-grado-temperature-converter) 
version: (0.0.0) 0.0.1
description: ULL ESIT Grado de Informática. 3º. PL. Lab "Temperature Converter"
entry point: (temperature.js) 
test command: open tests/index.html
git repository: (ssh://git@bitbucket.org/casiano/pl-grado-temperature-converter.git) 
keywords: regexp
author: Casiano
license: (ISC) 
About to write to /private/tmp/pl-grado-temperature-converter/package.json:

{
  "name": "pl-grado-temperature-converter",
  "version": "0.0.1",
  "description": "ULL ESIT Grado de Informática. 3º. PL. Lab \"Temperature Converter\"",
  "main": "temperature.js",
  "directories": {
    "test": "tests"
  },
  "scripts": {
    "test": "open tests/index.html"
  },
  "repository": {
    "type": "git",
    "url": "ssh://git@bitbucket.org/casiano/pl-grado-temperature-converter.git"
  },
  "keywords": [
    "regexp"
  ],
  "author": "Casiano",
  "license": "ISC"
}


Is this ok? (yes) y

Esto genera el fichero package.json:

[/tmp/pl-grado-temperature-converter(karma)]$ ls -ltr | tail -1
-rw-r--r--  1 casiano  wheel   487  5 feb 18:22 package.json
Si ahora escribo:
[/tmp/pl-grado-temperature-converter(karma)]$ npm test

> pl-grado-temperature-converter@0.0.1 test /private/tmp/pl-grado-temperature-converter
> open tests/index.html
Ejecutamos las pruebas en el navegador (en Mac OS X) supuesto que ya estuvieran escritas.

Pruebas: Mocha y Chai

Mocha is a test framework while Chai is an expectation one.

Mocha is the simple, flexible, and fun JavaScript unit-testing framework that runs in Node.js or in the browser.

It is open source (MIT licensed), and we can learn more about it at https://github.com/mochajs/mocha

Let's say Mocha setups and describes test suites and Chai provides convenient helpers to perform all kinds of assertions against your JavaScript code.

Pruebas: Estructura

Podemos instalar mocha globalmente:

$ npm install -g mocha
pero podemos también añadirlo en package.json como una devDependencies:
[/tmp/pl-grado-temperature-converter(karma)]$ head -n 5 package.json 
{
  "dependencies": {},
  "devDependencies": {
    "mocha": "latest"
  },

Y ahora podemos instalar todas las dependencias usando npm install:

$ npm install
npm http GET https://registry.npmjs.org/mocha
npm http 200 https://registry.npmjs.org/mocha
npm http GET https://registry.npmjs.org/commander/2.3.0
...

En este caso mocha es instalado localmente, no globalmente:

[/tmp/pl-grado-temperature-converter(karma)]$ ls -ltr node_modules/
total 0
drwxr-xr-x  12 casiano  staff  408  5 feb 18:40 mocha

Una vez instalado Mocha, creamos la estructura para las pruebas:

$ mocha init tests
esto en el caso de que lo hayamos instalado globalmente o bien
$ node_modules/mocha/bin/mocha init tests
si lo hemos instalado localmente.

$ tree tests
tests
|-- index.html
|-- mocha.css
|-- mocha.js
`-- tests.js

Añadimos chai.js (Véase http://chaijs.com/guide/installation/) al directorio tests.

Chai is a platform-agnostic BDD/TDD assertion library featuring several interfaces (for example, should, expect, and assert). It is open source (MIT licensed), and we can learn more about it at http://chaijs.com/

We can also install Chai on the command line using npm, as follows:

            npm install chai --save-dev

The latest tagged version will be available for hot-linking at http://chaijs.com/chai.js.

If you prefer to host yourself, use the chai.js file from the root of the github project at https://github.com/chaijs/chai.

[/tmp/pl-grado-temperature-converter(karma)]$ 
$ curl https://raw.githubusercontent.com/chaijs/chai/master/chai.js -o tests/chai.js
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  118k  100  118k    0     0  65521      0  0:00:01  0:00:01 --:--:-- 65500
Ya tenemos nuestro fichero tests/chai.js:
[/tmp/pl-grado-temperature-converter(karma)]$ head tests/chai.js 

;(function(){

/**
 * Require the module at `name`.
 *
 * @param {String} name
 * @return {Object} exports
 * @api public
 */
Quedando el árbol como sigue:
[~/srcPLgrado/temperature(master)]$ tree tests/
tests/
|-- chai.js
|-- index.html
|-- mocha.css
|-- mocha.js
`-- tests.js

0 directories, 5 files

Pruebas: index.html

Modificamos el fichero tests/index.html que fué generado por mocha init para

quedando así:
[~/srcPLgrado/temperature(master)]$ cat tests/index.html 
<!DOCTYPE html>
<html>
  <head>
    <title>Mocha</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="mocha.css" />
  </head>
  <body>
    <div id="mocha"></div>
    <input id="original" placeholder="32F" size="50">
    <span class="output" id="converted"></span>

    <script src="chai.js"></script>
    <script src="mocha.js"></script>
    <script src="../temperature.js"></script>
    <script>mocha.setup('tdd')</script>
    <script src="tests.js"></script>

    <script>
      mocha.run();
    </script>
  </body>
</html>

Pruebas: Añadir los tests

The "TDD" interface provides

[~/srcPLgrado/temperature(master)]$ cat tests/tests.js 
var assert = chai.assert;

suite('temperature', function() {
    test('32F = 0C', function() {
        original.value = "32F";
        calculate();
        assert.deepEqual(converted.innerHTML, "0.0 Celsius");
    });
    test('45C = 113.0 Farenheit', function() {
        original.value = "45C";
        calculate();
        assert.deepEqual(converted.innerHTML, "113.0 Farenheit");
    });
    test('5X = error', function() {
        original.value = "5X";
        calculate();
        assert.match(converted.innerHTML, /ERROR/);
    });
});

The BDD interface provides describe(), it(), before(), after(), beforeEach(), and afterEach():

describe('Array', function(){
  before(function(){
    // ...
  });

  describe('#indexOf()', function(){
    it('should return -1 when not present', function(){
      [1,2,3].indexOf(4).should.equal(-1);
    });
  });
});
The Chai should style allows for the same chainable assertions as the expect interface, however it extends each object with a should property to start your chain.

Chai Assert Style

The assert style is exposed through assert interface.

This provides the classic assert-dot notation, similiar to that packaged with node.js.

This assert module, however, provides several additional tests and is browser compatible.

var assert = require('chai').assert
  , foo = 'bar'
  , beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

assert.typeOf(foo, 'string', 'foo is a string');
assert.equal(foo, 'bar', 'foo equal `bar`');
assert.lengthOf(foo, 3, 'foo`s value has a length of 3');
assert.lengthOf(beverages.tea, 3, 'beverages has 3 types of tea');
In all cases, the assert style allows you to include an optional message as the last parameter in the assert statement.

These will be included in the error messages should your assertion not pass.

Assert API, Expect/Should API

Chai Expect Style

The BDD style is exposed through expect or should interfaces. In both scenarios, you chain together natural language assertions.

var expect = require('chai').expect
  , foo = 'bar'
  , beverages = { tea: [ 'chai', 'matcha', 'oolong' ] };

expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.length(3);
expect(beverages).to.have.property('tea').with.length(3);
Expect also allows you to include arbitrary messages to prepend to any failed assertions that might occur.

var answer = 43;

// AssertionError: expected 43 to equal 42.
expect(answer).to.equal(42);

// AssertionError: topic [answer]: expected 43 to equal 42.
expect(answer, 'topic [answer]').to.equal(42);
This comes in handy when being used with non-descript topics such as booleans or numbers.

Ejecución Simple

Ahora podemos ejecutar las pruebas abriendo en el navegador el fichero tests/index.html:
$ open tests/index.html

Esta información aparece también en las secciones Unit Testing: Mocha 11.4.1 de estos apuntes.

Manejando tareas en JS: Gulp

Why would you want to use a task runner?

They’re small applications that automate often time consuming and boring tasks.

If you ever need to do any of the following then a task runner is for you:

By creating a task file you can instruct the task manager to take care of many development tasks and watch for changes in relevant files. All you’ll need to do is start up the task runner and get to work on the more interesting parts of your project.

We are going to use gulp as our task manager. To install it you can:

Now we can write our gulpfile:

[/tmp/pl-grado-temperature-converter(karma)]$ cat gulpfile.js 
var gulp    = require('gulp'),
    gutil   = require('gulp-util'),
    uglify  = require('gulp-uglify'),
    concat  = require('gulp-concat');
var del     = require('del');
var minifyHTML = require('gulp-minify-html');
var minifyCSS  = require('gulp-minify-css');

gulp.task('minify', function () {
  gulp.src('temperature.js')
  .pipe(uglify())
  .pipe(gulp.dest('minified'));

  gulp.src('./index.html')
    .pipe(minifyHTML())
    .pipe(gulp.dest('./minified/'))

  gulp.src('./*.css')
   .pipe(minifyCSS({keepBreaks:true}))
   .pipe(gulp.dest('./minified/'))
});

gulp.task('clean', function(cb) {
  del(['minified/*'], cb);
});

Gulp on its own doesn’t do a lot. We need to install plugins and add tasks to the gulpfile to put Gulp into action. To concatenate files we’ll need the gulp-concat plugin; to install it run this from the command line:-

npm install gulp-concat --save-dev
the four Gulp methods that we will be using:- Again, if you check your package.json file you should see a new line referencing the newly installed plugin:-

"gulp-concat": "~2.1.7"

Después de instalar todas las dependencias:

$ npm i gulp-util --save-dev
$ npm i gulp-...  --save-dev
podemos ejecutar las tareas:
$ gulp minify
[22:07:58] Using gulpfile ~/local/src/javascript/PLgrado/temperature/gulpfile.js
[22:07:58] Starting 'minify'...
Que produce el directorio minified:
$ ls -l minified/
total 32
-rw-r--r--  1 casiano  staff   510  5 feb 21:56 global.css
-rw-r--r--  1 casiano  staff   594  5 feb 21:56 index.html
-rw-r--r--  1 casiano  staff  2021  5 feb 21:56 normalize.css
-rw-r--r--  1 casiano  staff   334  5 feb 21:56 temperature.js
que como vemos ha compactado los ficheros:
$ ls -l temperature.js normalize.css index.html global.css 
-rw-r--r--  1 casiano  staff   934  4 feb 09:11 global.css
-rw-r--r--  1 casiano  staff   749  3 feb 10:40 index.html
-rw-r--r--  1 casiano  staff  7798 30 ene 22:00 normalize.css
-rw-r--r--  1 casiano  staff   638  3 feb 15:21 temperature.js
Podemos ver la lista de tareas mediante la opción -T:
$ gulp -T
[22:00:40] Using gulpfile ~/local/src/javascript/PLgrado/temperature/gulpfile.js
[22:00:40] Tasks for ~/local/src/javascript/PLgrado/temperature/gulpfile.js
[22:00:40] |-- minify
[22:00:40] `-- clean
Podemos borrar las ficheros generados con gulp clean:
$ gulp clean
[22:00:46] Using gulpfile ~/local/src/javascript/PLgrado/temperature/gulpfile.js
[22:00:46] Starting 'clean'...
[22:00:46] Finished 'clean' after 7.68 ms
$ ls -l minified/
$

gulp has very few flags to know about. All other flags are for tasks to use if needed.

Tasks can be executed by running gulp <task> <othertask>.

Just running gulp will execute the task you registered called default.

If there is no default task gulp will error.

See CLI.md at gulpjs/gulp.

Pruebas: Véase

Casiano Rodríguez León
2016-03-27