luis v3.2.13
TL; DR;
Luis brings:
- React component development
- Seamless snapshot-based testing
- Snapshot management
- Web component catalogue
- Integration with VS Code and Wallaby.js ... and much more!
Introduction
LUIS (List of User Interfaces) is framework for collaborative building and testing React web components. It harnesses the power of FuseBox for fastest bundling, hot reloads, and out-of-the-box Typescript support. Following are stats for application with 976 typescript sources and 56 imported packages:
- StoryBook — Start
36 seconds
, Hot Module Reload with sourcemaps9 seconds
. - LUIS — Start
400 ms
, Hot Module Reload with sourcemaps750 ms
. Now that's what I call a significant difference.
Luis is using well known technologies (Jest, Mocha, React, optionally Wallaby) and methodologies (TDD, BDD, Snapshot testing), so there is almost nothing new to learn (apart from two new functions, storyOf and itMountsAnd).
To facilitate your component development, testing, and collaboration LUIS supports four different modes. Each mode is described in detail further below.
Quick Start
If you wish to run Luis only as component catalogue, similar to StoryBook, all you need to do is:
- Import package
- Import stories
- Render Luis UI
Start with the standard:
yarn add luis --dev
Next add your luis in package.json. You can either use only a luis
command that estimates your config file be located at src/luis.ts
, or you can provide your root
and path/to/luis.ts
as your parameters.
{
"scripts": {
"luis": "luis",
"luis-custom-path": "luis root-dir path/to/luis.ts"
}
}
If you are ok with a standard luis config all you need to do is to set up Luis and import all your stories.
// luis.ts
import { renderLuis, setupTestBridge } from 'luis';
// this needs to be there to set up custom global function that luis uses
// such as: storyOf
setupTestBridge();
// import all your stories and tests
import './client/modules/home/tests/example.test';
// render luis ui to '#react-root'
renderLuis();
And here is an example story:
import * as React from 'react';
storyOf('Component With Test', {
get component() {
return <div>My Component</div>;
}
});
If you want to know the full API os storyOf command, go to the API section.
Custom FuseBox Configuration
If you need a custom FuseBox config to package Luis, all you need to do is to define a luis.fuse.js
in your project root. WARNING: You need to define your homeDir
in fuse config with '../../'
prefix (e.g.homeDir: ../../src
. *I could not figure out how to convince require
to change root and start from a different directory. PRs welcome!*.
Importing Luis to an existing application
You can easily use Luis in your exisiting application under its own route (e.g. /louis
). Following is the tutorial to set this in the application with react-router.
// router
import { LuisView } from '../modules/luis';
...
<Route exact={true} path="/luis" component={LuisView()} />
...
And now the LuisView
component, where we import all our stories
// LuisView.tsx
import * as React from 'react';
import { Luis, setupTestBridge } from 'luis';
// this allows us to read storyOf commands
setupTestBridge();
// function makes sure the content hot-reloads
export function LuisView() {
// import all your stories
require('../home/tests/home_view.test');
return Luis;
}
Adding Tests
Luis works also as a sophisticated test result viewer. In this example, we will be working with Jest. Note that Luis also works with mocha with a related reporter. To allow Luis to display test results, we will export a test report after each test run. Therefore, in your jest.config.js
add:
module.exports = {
testResultsProcessor: 'luis/dist/bridges/jest/reporter',
...
}
The processor will save a test report after each test run and save it in your <project_root>/src/
folder. Both, summary (e.g. summary.json) and a list of detected snapshots (snapshots.js) is saved. If you want to enable snapshots and test reports in luis, you need to tell FuseBox to pack them into your bundle. This is done via SnapshotPlugin and JSONPlugin. Therefore, in your fuse.js
:
// fuse.js
const { FuseBox, ..., JSONPlugin } = require('fuse-box');
const { SnapshotPlugin } = require('luis/dist/bridges/jest/snapshot_plugin');
const fuse = FuseBox.init({
...
plugins: [
JSONPlugin(),
SnapshotPlugin()
],
sourceMaps: true
});
Now, we need to tell FuseBox, to pack them into our bundle. A good space for this is in the LuisView.tsx
file:
// LuisView.tsx (see above)
// adjust paths as necessary
const summary = require('../../../summary.json');
const snapshots = require('../../../snapshots');
setupTestBridge(summary, snapshots);
Now you are ready to visualise your tests in Luis. Make sure you run jest on server in watch mode. Yet, there he problem is, that Jest does not recognise storyOf
command. Therefore we create a new file jest.setup.js
and then modify the jest.config.js
to execute this file before each test run. Also, we need to tell jest to ignore the jest generated files.
// jest.setup.js
global.storyOf = function(name, props, impl) {
describe(name, () => impl && impl(props));
};
and
// jest.config.js
module.exports = {
...
"setupTestFrameworkScriptFile": "<rootDir>/jest.setup.js",
"watchPathIgnorePatterns": ['<rootDir>/src/summary.json', '<rootDir>/src/snapshots.js'],
}
If you are using wallaby, make sure to run jest.setup.js
as well
THAT'S IT! ENJOY!
Luis Interface
The main buttons of Luis interface perform following actions:
- Tree mode / flat mode changes the way the list of tests are displayed
- Configuration allows you to enable / disable specific tests and more
- Update button updates current snapshot to the new version and saves it on your drive
- Auto-update toggle allows to update snapshot automatically when tests are run. This is very useful during component development and writing of tests.
Luis has four view modes:
- React: Displays a "live" React components, used mostly during component development
- Html: Displays a HTML version of the snapshot and shows side-by-side comparison if snapshots differ.
- Json: Displays raw source of the snapshot and compares the differences
- Snapshots: Show all saved snapshots for a current test
Tree view shows all tests and snapshots. It also shows all test results, and if possible, it shows side-by-side comparison of actual vs. expected value. The number next to the test item represent the execution time of the test. When number is:
- Green - all tests pass
- Orange - some tests pass
- Red - all tests fail
The exact functionality of each button is shown below:
API
The API of Luis is dead simple. It uses classic testing methodology using describe, it, before, beforeEach, beforeAll, after, afterEach, afterAll
and xit
for skipping tests.
The specific significance has describe
function, which represents a folder
in luis and it is rendered accordingly in the test tree. The new functions are storyOf
and itMountsAnd
and matcher matchSnapshot
.
storyOf
We have borrowed the naming from the very popular Storybook package. The storyOf
function is an extension of the describe
and its purpose is to define a React component and all the tests with snapshots. Followiong is a definition of storyOf
:
interface StoryConfig {
[index: string]: any;
component: JSX.Element;
info?: string;
cssClassName?: string;
componentWithData?(
...props: any[]
):
| JSX.Element
| {
[index: string]: any;
component: JSX.Element;
afterMount?(wrapper: ReactWrapper): void;
};
}
function storyOf<T extends StoryConfig>(
name: string,
config: T,
implementation: (params: T) => void
): void;
The only compulsory parameter of the config
part of the storyOf
is component
, which needs to return a JSX.Element
, for example <div>Luis</div>
. The info
stores the description of the story, and cssClassName
adds a css class to the element which will wrap your rendered React component. componentWithData
is a very versatile function that allows you to define variations of your component (examples below) and modify the component after it has been mounted. Following is an example of the storyOf
function:
storyOf(
'My Component',
{
someData: 1,
get component() {
return <div>My component</div>;
}
},
function({ someData, component }) {
it('mounts component', function() {
const wrapper = mount(component); // now do some tests
});
it('tests', function() {
expect(someData).toEqual('1');
});
}
);
Adding Test Files
If you add a new test file, you need to import to src/example/luis
. This is the start file of Luis project. This can be changed in fuse.js
file.
import { renderLuis } from '../client/components/index';
import './tests/foo.test';
import './tests/bar.test';
import './tests/boo.test';
renderLuis();
Visual Studio Extension: Luis
The Extension for Visual Studio Code comes with two awesome functionalities:
You can visualise current snapshot directly in Code environment. Just press
CMD + P
and search fromLuis: Snapshot Preview
. The snapshot will automatically load snapshots from the current test. This functionality works really well with automated test runner such as wallabyjs, or mocha or jest in watch test mode, and with snapshot delivery over TCP, since snapshots automatically change as you type.You can work directly with a React component which is hot reloaded into your envirnment. Just press
CMD + P
and search fromLuis: Component Preview
. For this to work, you need to first run Luis (npm start luis
). If you need to access the development console of the previewed component pressCMD + P
and search forLuis: Show Componnt Dev Tools
. The previewed component automatically changes based on your selected test. The simplified interface provides following functionality:- See test result and test run time
- Visualise the React component and manually test its functionality (great for development)
- Visualise the difference between current component state and saved snapshot
- Visualise the code difference between current component and snapshot
- Update snapshot
- Set automatic snapshot update with each hot reload
Troubleshooting
If at any point things start to go sideways, try deleting the .fusebox folder in the root of your project and restart Luis.
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago