|
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