Testing a sample React application with Siesta
===============

In this guide, we'll describe how to use Siesta with React application. As a sample application, we'll use 
[GFontsSpace](https://github.com/pankajladhar/GFontsSpace). It is built with [react-scripts](https://www.npmjs.com/package/react-scripts)
- a modern web application framework.  

This application already has tests, which runs in Node.js. It is arguable better, however, to run the tests on 
the same platform used by the end-user, so in this Siesta setup, we will aim to run application tests in real browsers.
 
We won't write any new tests, instead, we "port" some of the existing ones to Siesta.

In the following "Live example" sections there are instructions how to complete the setup of the example and examine it "live" in the browser.

The 'Setup details' section, contains the detailed description of all changes, made to the "stock" GFontsSpace app.


Live example
------------

The sample React application is bundled in the Siesta examples in the directory `examples-browser/react-google-fonts-space`. It is also available
as a git repository, forked from the original app: [git://git.assembla.com/bryntum.siesta-react-gfonts-example.git]()

To complete the setup, switch to the example folder in console, and run:

    > npm install
    
Then to launch the server-side part of the example:

    > npm run start-siesta

This command will open a Chrome window, pointing to `http://localhost:3000/`. There will be a running application in it:

{@img images/gfonts-app.jpg}

If you open the `http://localhost:3000/siesta.html` then you'll see Siesta's web interface, from which you can launch individual tests.

{@img images/siesta-web-interface.jpg}

There are 3 groups of tests - "Model", "Individual components", "Black box". This is a <a href="#!/guide/testing_strategy">recommended strategy</a> 
for testing your web application, which has the best ROI for time in our experience.  


Setup details
--------------

Ok, we start we with the "stock" [GFontsSpace](https://github.com/pankajladhar/GFontsSpace) app, freshly cloned to some directory on your machine, which
is assumed to be a current directory for the all console commands we'll use.


###Preparations

To complete the clone:

    > npm install 

First we need to "eject" the application, to get access to the fine-grained configuration files and options. For that:

    > npm run eject

Then, we add dependency on [siesta-lite](https://www.npmjs.com/package/siesta-lite) and couple of webpack plugins in `package.json`:

```json
    "devDependencies": {
        ...
        
        "siesta-lite": "^5.2.0",
        
        "webpack-watched-glob-entries-plugin": "^2.1.2",
        "copy-webpack-plugin": "^4.5.2"
    }
``` 

Then we install them:

    > npm install 


### Siesta project creation

Now, lets follow in general the <a href="#!/guide/getting_started_browser">Getting started with Siesta in browser environment</a> guide and create the
Siesta project file as the `/src/siesta.js`, near the `/src/index.js` entry to the application itself:

```javascript
const Siesta    = require('siesta-lite/siesta-all.js')

const project   = new Siesta.Project.Browser();

project.configure({
    title       : 'React Google Fonts Space test suite'
});

project.plan(
    {
        // Tests in this group, exercises model - the data layer of the app
        group       : 'Model',

        items       : [
            './TRANSPILED/src/tests/model/mapper.t.js',
        ]
    },
    {
        // Tests in this group, exercises individual components
        group       : 'Individual components tests',

        items       : [
            './TRANSPILED/src/tests/components/text-box.t.js',
            './TRANSPILED/src/tests/components/color-picker.t.js'
        ]
    },
    {
        // Tests in this group, access application as a "black box"
        // they open the `index.html` page and then executes as a
        // script on that page
        group       : 'Black box tests',

        pageUrl     : 'index.html',

        items       : [
            './TRANSPILED/src/tests/blackbox-app/010-sanity.t.js'
        ]
    }
);

project.start();
```

In the spirit of WebPack development, we "require" the `siesta-all.js` file, instead of loading it with the `<script>` tag. Our experiments with bundling
Siesta's web interface CSS files in the same manner did not succeed though.

That's why in the html wrapper for the Siesta project, we include them "normally", with the plain-old `<link>` tag. Lets name the wrapper file `/public/siesta.html`,
again, it will be near the `public/index.html` file of the application entry.

```html
<!DOCTYPE html>
<html>
    <head>
        <!-- Recommended set of pragmas, required for IE11 compatibility-->
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

        <!-- Web interface styles -->
        <link rel="stylesheet" type="text/css" href="static/siesta-lite/resources/css/siesta-all.css">

    </head>

    <body>
    </body>
</html>
```

We'll see shortly, how the "static/siesta-lite/resources/css" folder will be made available to the Siesta web interface page.


### Plugging into Webpack pipeline

We now have the entry files to the Siesta web interface, but need to include them in the same WebPack pipeline that process files in the `src/` folder.

That pipeline is defined in the `config/webpack.config.dev.js`. We will use it as a basis for two other pipelines we create. Lets create a new webpack 
config as `config/webpack.config.siesta-dev.js`:

Import some infrastructure:
  
```javascript
'use strict';

const path                  = require('path');
const HtmlWebpackPlugin     = require('html-webpack-plugin');
const paths                 = require('./paths');

const CopyWebpackPlugin         = require('copy-webpack-plugin');
const WebpackWatchedGlobEntries = require('webpack-watched-glob-entries-plugin');

const lodash                = require('lodash');
```

Import the default WebPack config `webpack.config.dev.js` and tweak it - set the path for the output bundle to `build` folder. This is
needed if you want to build a filesystem snapshot of the project (by default project files are served from memory with the development server).

```javascript
const regularDev            = require('./webpack.config.dev.js')

// patch to always output to "build" directory (works fine with "npm start")
regularDev.output.path      = paths.appBuild
```

Then we create a new WebPack config, by cloning the dev one, and change the entry to point the Siesta project file, and output - to the `/static/siesta` folder

```javascript
const siestaWebInterface    = lodash.cloneDeep(regularDev)

siestaWebInterface.entry        = [
    paths.siestaProject || (paths.appSrc + '/siesta.js')
]
siestaWebInterface.output.filename = 'static/siesta/bundle.js'
```

We remove the `HtmlWebpackPlugin` plugin from the cloned config - as we don't want this pipeline to generate the `index.html` , and instead 
add our own plugins - `HtmlWebpackPlugin`, pointing to the `public/siesta.html` template, we've created earlier. 

In that template, we've used the `static/siesta-lite/resources` folder from the `siesta-lite` package,
so also copy it to the distribution, using the `CopyWebpackPlugin` plugin:

```javascript
lodash.remove(siestaWebInterface.plugins, plugin => plugin instanceof HtmlWebpackPlugin)

siestaWebInterface.plugins.push(
    new HtmlWebpackPlugin({
      inject        : true,
      filename      : 'siesta.html',
      template      : paths.siestaHtml || 'public/siesta.html'
    }),
    new CopyWebpackPlugin([
        { from : 'node_modules/siesta-lite/resources', to : 'static/siesta-lite/resources' }
    ])
)
```

This is all for the Siesta web interface page, now we also want to setup the transpilation for the individual tests.

For that, we use [WebpackWatchedGlobEntries](https://www.npmjs.com/package/webpack-watched-glob-entries-plugin) plugin,
providing a glob, matching all the Siesta test files `*.t.js` in the `src/tests/**/`. We then setup an output name,
so that filesystem structure is reflected in the `TRANSPILED` folder of the resulting build:  

```javascript
const testsTranspilation    = lodash.cloneDeep(regularDev)

testsTranspilation.entry    = WebpackWatchedGlobEntries.getEntries(
    [
        path.resolve(__dirname, '../src/tests/**/*.t.js'),
    ],
    {
      // // Optional glob options that are passed to glob.sync()
      // ignore: '**/*.test.js'
    }
)
testsTranspilation.output.filename = 'TRANSPILED/src/tests/[name].js'
```

Then we again, remove the `HtmlWebpackPlugin` plugin, and include our own:

```javascript
lodash.remove(testsTranspilation.plugins, plugin => plugin instanceof HtmlWebpackPlugin)

testsTranspilation.plugins.push(
    new WebpackWatchedGlobEntries()
)
```

Finally, export all 3 webpack pipelines from our config file:

```javascript
module.exports = [
    regularDev,
    siestaWebInterface,
    testsTranspilation
];
```
And, lets create a copy of the `scripts/start.js` - `scripts/start-siesta.js`. The only line we change in it:

```javascript
const config = require('../config/webpack.config.dev');
```

To:

```javascript
const config = require('../config/webpack.config.siesta-dev');
```

We need to also add it to the `package.json`:

```json
"scripts": {
    "start-siesta": "node scripts/start-siesta.js",
},
```

### Open Siesta web interface

Now, we can use the `start-siesta.js` script, to launch the WebPack development server:
    
    > npm run start-siesta 

It will serve all 3 pipelines, we created in the previous section. Among them, there's a Siesta web interface webpage, available on the
`http://localhost:3000/siesta.html`:

{@img images/siesta-web-interface.jpg}

From the web interface one can launch individual tests. Normally, WebPack takes care of auto-updating the sources, however when some drastical
changes to the source folders structure, one may need to restart it.
  

### Porting test files

Let examine how one Siesta tests compares with original tests of this application. The original one:

```javascript
let mapper = require('./../index');
import fetchMock from 'fetch-mock';
import { googleFontResponse } from './../../TestData/MockGoogleFontResponse';

describe("Mapper", ()=>{
    it("should map respose properly", ()=>{
        fetchMock.get('*', googleFontResponse);
        mapper.fetchGoolgeFonts().then((obj)=>{
            expect(obj.categories).toEqual(["All", "sans-serif", "serif", "display"]);
            expect(Object.keys(obj.fonts).length).toEqual(4);
            expect(obj.fonts["handwriting"]).toBeUndefined();
            
            expect(obj.fonts["All"].length).toEqual(8);
            expect(obj.fonts["All"][0]).toEqual({"label": "ABeeZee", "value": "ABeeZee", "variants": [{"label": "regular", "value": "regular"}, {"label": "italic", "value": "italic"}]});

            expect(obj.fonts["sans-serif"].length).toEqual(5);
            expect(obj.fonts["serif"].length).toEqual(2);
            expect(obj.fonts["display"].length).toEqual(1);
        })
    })
})
```

Its Siesta version:

```javascript
import fetchMock from 'fetch-mock';
import { googleFontResponse } from './../../TestData/MockGoogleFontResponse';

let mapper = require('../../Mapper');

describe("Mapper", t => {
    t.it("should map respose properly", t => {
        fetchMock.get('*', googleFontResponse);
        // return the promise from the test function, to wait for it resolution
        return mapper.fetchGoolgeFonts().then(obj => {
            t.expect(obj.categories).toEqual(["All", "sans-serif", "serif", "display"]);
            t.expect(Object.keys(obj.fonts).length).toEqual(4);
            t.expect(obj.fonts["handwriting"]).toBeUndefined();

            t.expect(obj.fonts["All"].length).toEqual(8);
            t.expect(obj.fonts["All"][0]).toEqual({"label": "ABeeZee", "value": "ABeeZee", "variants": [{"label": "regular", "value": "regular"}, {"label": "italic", "value": "italic"}]});

            t.expect(obj.fonts["sans-serif"].length).toEqual(5);
            t.expect(obj.fonts["serif"].length).toEqual(2);
            t.expect(obj.fonts["display"].length).toEqual(1);
        })
    })
})
```

As you can see it maps pretty straightforward. We won't dive into this topic, there's more information available in the "Getting started guides",
in this guide we are primarily interested in the overall project setup.


Conclusion
----------

As you can see, Siesta can be plugged into React application in very straightforward way. You should be able
to follow the same approach in your application, even if it does not use `react-scripts`.  

See also <a href="#!/guide/event_recorder">Using the event recorder</a> guide, in which this sample application is used to record 
some user actions.

If you will encounter any issues or problems with your React testing, please let us know in the forums below, and we'll try to help you.

Happy testing!


Buy this product
---------

Visit our store: <https://bryntum.com/store/siesta>


Support
---------

Ask question in our community forum: <https://www.bryntum.com/forum/viewforum.php?f=20>

Subscribers can post expedited questions in Premium Forum: <https://www.bryntum.com/forum/viewforum.php?f=21>

Please report any bugs through the web interface at <https://www.assembla.com/spaces/bryntum/support/tickets>


See also
---------

Siesta web-page: <https://bryntum.com/products/siesta>

Other Bryntum products: <https://bryntum.com/products>



Attribution
---------

This software contains icons from the following icon packs (licensed under Creative Common 2.5/3.0 Attribution licenses)

- <http://www.famfamfam.com/lab/icons/silk/>
- <http://led24.de/iconset/>
- <http://p.yusukekamiyamane.com/>
- <http://rrze-icon-set.berlios.de/index.html>
- <http://www.smashingmagazine.com/2009/05/20/flavour-extended-the-ultimate-icon-set-for-web-designers/>
- <http://www.doublejdesign.co.uk/products-page/icons/super-mono-icons/>
- <http://pixel-mixer.com/>

Thanks a lot to the authors of the respective icons packs.


COPYRIGHT AND LICENSE
---------

Copyright (c) 2009-2018, Bryntum & Nickolay Platonov

All rights reserved.
