|
1 | | -# marko-tester [](https://travis-ci.org/oxala/marko-tester) |
| 1 | +[](https://travis-ci.org/oxala/marko-tester) [](https://codeclimate.com/github/oxala/marko-tester/test_coverage) |
| 2 | +# marko-tester |
| 3 | +A utility that helps you test marko components within the JEST framework. |
2 | 4 |
|
3 | | -Test library to assist with testing marko 4 (and marko 4 legacy) UI components and more. |
4 | | - |
5 | | -## Usage |
6 | | - |
7 | | -### Start using marko-tester with: |
8 | | - |
9 | | -``` |
10 | | -yarn add marko-tester --dev |
11 | | -``` |
12 | | - |
13 | | -### CLI |
14 | | - |
15 | | -Once you've installed marko-tester, you can start using the `markotester` alias with the path to your source folder. There are few arguments you can pass if needed: |
16 | | - |
17 | | -- `--no-coverage` if you don't want to generate coverage report |
18 | | -- `--no-mocha` if you want to execute only linting |
19 | | -- `--no-lint` if you don't want lint checks |
20 | | -- `--lint-es5` if you want to lint code with es5 rules by default |
21 | | -- `--fix-lint` if you want to automatically fix your linting issues |
22 | | -- `--fix-fixtures` if you want to automatically replace failing fixtures with actual render result |
23 | | -- `--fix` combines `--fix-lint` and `--fix-fixtures` together |
24 | | - |
25 | | -``` |
26 | | -markotester source --no-coverage |
27 | | -markotester source --no-coverage --no-lint |
28 | | -markotester source --fix-lint |
29 | | -markotester source --fix-fixtures |
30 | | -``` |
31 | | - |
32 | | -`package.json` example: |
| 5 | +## Requirements |
| 6 | +Your project needs to have `jest@^23` and `marko@^4.5` installed. |
| 7 | +Within your regular JEST configuration, you need to specify a transform for `*.marko` files: |
33 | 8 |
|
34 | 9 | ``` |
35 | | -"scripts": { |
36 | | - "lint": "yarn test --lint --no-mocha", |
37 | | - "test": "markotester src --no-lint --no-coverage", |
38 | | - "coverage": "yarn test --coverage" |
| 10 | +... |
| 11 | +"transform": { |
39 | 12 | ... |
| 13 | + "^.+\\.marko$": "<rootDir>/node_modules/marko-jest/preprocessor.js" |
40 | 14 | } |
41 | 15 | ``` |
42 | 16 |
|
43 | | -### File structure |
44 | | - |
45 | | -``` |
46 | | -app |
47 | | -|- source |
48 | | -| |- components |
49 | | -| | |- phone-frame |
50 | | -| | | +- test |
51 | | -| | | |- fixtures |
52 | | -| | | | |- default.html |
53 | | -| | | | |- default.json |
54 | | -| | | | |- empty.js |
55 | | -| | | | +- empty.html |
56 | | -| | | +- index.spec.js |
57 | | -| | |- component.js |
58 | | -| | +- index.marko |
59 | | -| +- pages |
60 | | -| | +- mobile-preview |
61 | | -| | |- test |
62 | | -| | | |- fixtures |
63 | | -| | | | |- default.html |
64 | | -| | | | +- default.json |
65 | | -| | | +- index.spec.js |
66 | | -| | |- index.js |
67 | | -| | +- index.marko |
68 | | -| +- services |
69 | | -| |- test |
70 | | -| | +- amazing-service.spec.js |
71 | | -| +- amazing-service.js |
72 | | -+- .marko-tester.js |
73 | | -``` |
74 | | - |
75 | | -### Configuration file |
76 | | - |
77 | | -You can find an example configuration file in the root folder of `marko-tester` (default confirugation): |
| 17 | +## Configuration |
| 18 | +In the global JEST object, you can pass a `tester` configuration: |
78 | 19 |
|
79 | 20 | ``` |
80 | | -{ |
81 | | - "components": [], |
82 | | - "taglibExcludeDirs": [], |
83 | | - "taglibExcludePackages": [], |
84 | | - "excludedAttributes": [], |
85 | | - "lassoPlugins": [], |
86 | | - "coverage": { |
87 | | - "reporters": [ |
88 | | - "text-summary", |
89 | | - "html", |
90 | | - "json-summary" |
91 | | - ], |
92 | | - "dest": ".reports", |
93 | | - "excludes": [ |
94 | | - "**/*.marko.js" |
95 | | - ] |
| 21 | +"global": { |
| 22 | + ... |
| 23 | + "tester": { |
| 24 | + "fixturesDir": "inputs", |
| 25 | + "shallow": false |
96 | 26 | } |
97 | | -}; |
| 27 | +} |
98 | 28 | ``` |
99 | 29 |
|
100 | | -* **components** - An array of patterns for files that should be loaded into jsdom page by lasso. |
101 | | -* **taglibExcludeDirs** - An array of paths relative to the root of your project folders that contain `marko.json`. This is used to isolate your tests so the nested components won't be renderer. |
102 | | -* **taglibExcludePackages** - An array of module names. This is used to isolate your tests so the nested components won't be renderer. |
103 | | -* **excludedAttributes** - An array of HTML attributes that can be different every test execution (e.g `data-widget` which marko dynamically changes based on package version). |
104 | | -* **lassoPlugins** - An array of lasso plugins to require and attach to lasso configuration during client test execution. |
105 | | -* **coverage.reporters** - An array of reporters for istanbul to use. |
106 | | -* **coverage.dest** - The target destination folder where reports will be written. |
107 | | -* **coverage.excludes** - An array of file patterns to exclude from istanbul reports. |
108 | | - |
109 | | -### Automatic component/fixtures search and fixtures test |
| 30 | +- fixturesDir - The folder name where you have fixtures to render the component with. _(Default: "fixtures")_ |
110 | 31 |
|
111 | | -Marko-tester will try to automatically find your component renderer and/or fixtures to test. For the renderer, marko-tester will go up one level from your spec file and search for `index.marko` (or the file specified in `w-bind` for legacy components). |
| 32 | +- shallow - You can turn off shallow rendering by passing `false` here. That way marko won't isolate any component test. _(Default: true)_ |
112 | 33 |
|
113 | | -Fixtures will be automatically found if they are inside the `fixtures` folder on the same level as your spec file. |
114 | | - |
115 | | -If fixtures and renderer would be found, and spec file exists for the component,fixture test would be automatically performed. |
| 34 | +## Usage |
| 35 | +`marko-tester` exposes a `getComponent` method for you to use. Pass a relative test file path to a marko component and you will receive a `render` method and `fixtures` method/object. By default, `getComponent` will run JEST SnapShot tests for the fixtures of the component. |
116 | 36 |
|
117 | | -### Render comparison based on specific input (fixtures test) |
| 37 | +- `render` is a method that renders a component with a given input and mounts it to `document.body`. The mounted component instance is returned. |
118 | 38 |
|
119 | | -The rendering test works by giving your template the input to use for rendering and then comparing output with the specified HTML. |
| 39 | +- `fixtures` is an object by default. It contains all the fixtures that are found within the fixture folder of this component. If a `withoutFixtures` option is passed to the `getComponent` method, `fixtures` will be a method that will run JEST SnapShot tests for your fixture. You will still be able to get fixture content by the filename: `fixtures[FixtureFileName]`. |
120 | 40 |
|
121 | | -The JSON file and HTML file comprising a test should follow the pattern below (check the `fixtures` folder in File Structure section): |
| 41 | +### Example |
| 42 | +You can find examples in the `tests` folder. The boilerplate looks like this: |
122 | 43 |
|
123 | 44 | ``` |
124 | | -{test-case}.html |
125 | | -{test-case}.json |
126 | | -{another-test-case}.html |
127 | | -{another-test-case}.js |
128 | | -``` |
| 45 | +const { getComponent } = require('marko-tester'); |
| 46 | +const { render } = getComponent('../index.marko'); |
129 | 47 |
|
130 | | -### Component client-side testing |
| 48 | +describe('When component is rendered', () => { |
| 49 | + let component; |
131 | 50 |
|
132 | | -The client test works by instantiating a marko-widget and testing the functionality against it. For that browser environment is needed, for those purposes marko-tester uses jsdom to render the lasso-generated page and expose window object. |
133 | | - |
134 | | -During client testing, `marko-tester` gives you a few methods to utilize: |
135 | | - |
136 | | -* **describe.page** - Will create an empty page, giving you access to window and document objects. This method is available right after test case declaration. |
137 | | -* **describe.component** - Used to build the page with the component constructor in it. At this point, the `marko.component` attribute will be exposed to the mocha context giving you access to your widget's instance. This method is available right after test case declaration. |
138 | | - |
139 | | -``` |
140 | | -'use strict'; |
141 | | -
|
142 | | -// First describe in spec files will be read by marko-tester to initialize test environment; |
143 | | -// By default the describe string will be the path to autodiscovered renderer or `index.js` in the directory above; |
144 | | -// If you specify a string for first describe, your text will be appended to the path of the directory above; |
145 | | -describe(({ expect, sinon, fixtures }) => { |
146 | | - // list of the params that are being returned in the callback: |
147 | | - // expect - chai's expect; |
148 | | - // sinon - library to spy and stub; |
149 | | - // fixtures - will give you a list of attached test fixtures to current component; |
150 | | - // mockRequire - exposes 'mock-require' npm module; |
151 | | -
|
152 | | - describe.component(({ marko, modRequire }) => { |
153 | | - // list of the params that are being returned in the callback: |
154 | | - // modRequire - a helper function to require modules on a browser level; |
155 | | - // marko - marko context that contains the component instance under `marko.component`; |
156 | | -
|
157 | | - let mockHello; |
158 | | -
|
159 | | - beforeEach(() => { |
160 | | - mockHello = 'world'; |
161 | | - marko.component.hello = mockHello; |
162 | | - }); |
163 | | -
|
164 | | - afterEach(() => { |
165 | | - delete marko.component.hello; |
166 | | - }); |
167 | | -
|
168 | | - it('should have hello attribute', () => { |
169 | | - expect(marko.compomnent.hello).to.be.equal(mockHello); |
170 | | - }); |
| 51 | + beforeEach(() => { |
| 52 | + component = render(fixtures.default); |
171 | 53 | }); |
172 | | -}); |
173 | | -``` |
174 | | - |
175 | | -By default, running `describe.component` will build the component using the `default` fixture (if there is one). If you wish to build the component using a different fixture, you can pass an option to do that before the callback: |
176 | | - |
177 | | -``` |
178 | | -describe.component({ |
179 | | - fixture: {} |
180 | | -}, ({ marko }) => { ... }); |
181 | | -``` |
182 | | - |
183 | | -### Few additional features |
184 | | - |
185 | | -1. `describe.component` and `describe.page` commands are just patched describe functions. That's why the `only` and `skip` operators can be used with these commands (e.g `describe.component.only()`, `describe.page.skip()`). |
186 | 54 |
|
187 | | -2. If you want to mock require during client-side testing - you can do that using options for `testComponent` method. There as a key you can pass relative path to the necessary file that will be required. And the mock of that file as a value. Keep in mind that mocked require will only exist within this `buildComponent`.<br> |
188 | | -``` |
189 | | -describe.component({ |
190 | | - mock: { |
191 | | - require: { |
192 | | - '../dep': { hello: 'world' }, |
193 | | - some_node_module: { world: 'hello' } |
194 | | - }, |
195 | | - component: { |
196 | | - 'nested-component': { world: 'hello' } |
197 | | - }, |
198 | | - components: { |
199 | | - 'hello-worlds': [{ worlds: 'hello' }] |
200 | | - } |
201 | | - }, |
202 | | -}, ({ marko }) => { ... }); |
203 | | -``` |
| 55 | + afterEach(() => { |
| 56 | + component.destroy(); |
| 57 | + }); |
204 | 58 |
|
205 | | -3. You can also use a different file layout if necessary. When your template has a top-level element of `tbody`, `tr`, or something else that expects a `table` element as a parent, you can add the `layout` parameter and set it to `table`. This will ensure JSDOM renders your component correctly. |
206 | | -``` |
207 | | -describe.component({ |
208 | | - fixture: fixtures.basic, |
209 | | - layout: 'table' |
210 | | -}, ({ marko }) => { ... }); |
| 59 | + ...your assertions... |
| 60 | +}); |
211 | 61 | ``` |
212 | 62 |
|
213 | | -## Code style (linting) |
214 | | - |
215 | | -Apart from testing, consistent styling is another important part of keeping high quality code. For that particular reason, `marko-tester` comes with an `eslint` and `stylelint` checks built-in. It will check the style of your code when you execute the `markotester` command. |
216 | | - |
217 | | -It uses legacy (es5) **airbnb** configuration for ESLint and **standard** configuration for Stylelint (checkine both *less* and *css* files). |
218 | | - |
219 | 63 | ## References |
220 | | - |
221 | 64 | * [Marko](http://markojs.com) |
222 | | -* [Mocha](https://mochajs.org) |
223 | | -* [Sinon](http://sinonjs.org/docs/) |
224 | | -* [Expect](http://chaijs.com/api/bdd/) |
225 | | -* [rewire](https://github.com/jhnns/rewire) |
226 | | -* [mock-require](https://github.com/boblauer/mock-require) |
227 | | -* [ESLint](http://eslint.org) |
228 | | -* [eslint-airbnb-config](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) |
229 | | -* [eslint-config-ebay](https://github.com/darkwebdev/eslint-config-ebay) |
230 | | -* [Stylelint](https://github.com/stylelint/stylelint) |
231 | | -* [stylelint-config-standard](https://github.com/stylelint/stylelint-config-standard) |
232 | | -* [Istanbul](https://github.com/gotwarlost/istanbul) |
| 65 | +* [jest](https://jestjs.io) |
| 66 | + |
| 67 | +## Thanks |
| 68 | +* [Dylan Piercey](https://github.com/DylanPiercey) |
| 69 | +* [Abiyasa Suhardi](https://github.com/abiyasa) |
| 70 | + |
| 71 | +## Licence |
| 72 | +MIT |
0 commit comments