UNPKG

17.8 kBMarkdownView Raw
1# util.fixture
2
3> Test fixture library
4
5[![build](https://circleci.com/gh/jmquigley/util.fixture/tree/master.svg?style=shield)](https://circleci.com/gh/jmquigley/util.fixture/tree/master)
6[![analysis](https://img.shields.io/badge/analysis-tslint-9cf.svg)](https://palantir.github.io/tslint/)
7[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
8[![testing](https://img.shields.io/badge/testing-jest-blue.svg)](https://facebook.github.io/jest/)
9[![NPM](https://img.shields.io/npm/v/util.fixture.svg)](https://www.npmjs.com/package/util.fixture)
10[![coverage](https://coveralls.io/repos/github/jmquigley/util.fixture/badge.svg?branch=master)](https://coveralls.io/github/jmquigley/util.fixture?branch=master)
11
12A testing fixture class used to simplify managing testing artifacts. A testing fixture in this context is set of files and directories that are copied/loaded into a temporary location, named template values within the files are replaced, the fixture is used for that test, and then it is destroyed when all tests are complete. The constructor looks for fixtures from the root of the project in `./test/fixtures`. This directory contains a set of additional directories. Each subdirectory represents a named fixture that can be used in a test. e.g.
13
14 ./__tests__/fixtures/some-test
15
16This would contain a usable fixture named `some-test`. The name of the fixture is a parameter to the constructor. Within these named fixture directories one can place files and directories used by a test. See the usage section below on how to use this within a test.
17
18The reason for this module is to deal with concurrency in the [ava] test runner. It runs tests concurrently, so using one directory for test fixtures is a problem as the tests will share artifacts incorrectly (think of two tests trying to access the same file and writing different things at the same time). This overcomes that issue by making a separate temporary location each time a fixture is instantiated; different tests will each have their own copy of the fixture.
19
20#### Features
21- Template replacement
22- Automatic parsing of a JSON file within the fixture
23- Parsing of a data file list
24- Can be used with concurrent test processing
25- Execution of a fixture.js script during instantiation
26- Lorem Ipsum and Pattern generators
27
28## Installation
29
30This module uses [yarn](https://yarnpkg.com/en/) to manage dependencies and run scripts for development.
31
32To install as an application dependency with cli:
33```
34$ yarn add --dev util.fixture
35```
36
37To build the app and run all tests:
38```
39$ yarn run all
40```
41
42## Usage
43
44#### Simple Fixture
45```javascript
46const Fixture = require('util.fixture');
47
48let fixture = new Fixture('test-fixture-1');
49let s = fixture.read('somefile.txt');
50
51... // your test
52```
53
54This will copy the contents of the named fixture `test-fixture-1` to a temporary location. The temporary directory location is based on the environment variables `TMP` or `TEMP`. If neither of these are set, then the directory `~/.tmp` is chosen (and created if it doesn't exist). This is the home directory of the user running the test. Within this directory another directory is created named `unit-test-data`. All test fixtures are copied to this location when used. The name of each fixture is a generated UUID to make them unique for each test each time it is exectued. When a new fixture is created it returns an object with attributes related to that fixture (the *attributes* are listed below). The structure of the fixture could be:
55
56```
57./__tests__/fixtures/test-fixture-1/
58 somefile.txt
59 somedirectory/
60 ...
61```
62
63In this example the temporary location `fixture.dir` represents the temporary directory where the fixture was copied and expanded. From this location one would see the directory/file structure above. The creation of the fixture also results in template replacement. In this example there are no custom templates variables; only builtins (template replacement will be explained below).
64
65This example also shows that a file named `somefile.txt` was read from the fixture into a temporary string variable `s` using the `read()`.
66
67
68#### Simple JSON
69```javascript
70const Fixture = require('util.fixture');
71
72let fixture = new Fixture('simple-json');
73
74... // your test
75```
76
77Similar to a simple fixture above. If the fixture contains a file with the name `obj.json` then it will load the fixture with this JSON file, parse this JSON, perform template replacement, and save it within the fixture in an exposed field named `fixture.obj` and `fixture.jsonObj` (these will reference the same object). The structure of the fixture would be:
78
79```
80./__tests__/fixtures/simple-json/
81 obj.json
82 somedirectory/
83 ...
84```
85
86One can see the contents of the JSON file with the property `fixture.json`. This is the string data read from the given JSON file.
87
88
89#### JSON with Template Replacement
90```javascript
91const Fixture = require('util.fixture');
92
93let fixture = new Fixture('some-fixture', {
94 templateData: {
95 replaceMe: 'test data'
96 }
97});
98
99... // your test
100```
101
102Loads a JSON file saved in a fixture location and replaces custom text strings using template replacement. This would search the given JSON file for all instances of the string `{replaceMe}` and substitute the given value in the template (`test data`). An example JSON in this case would be:
103
104```json
105{
106 "testData": "{replaceMe}",
107 "testBool": true
108}
109```
110
111resulting in:
112
113```json
114{
115 "testData": "test data",
116 "testBool": true
117}
118```
119
120It will lead to a fixture object returned like the previous two examples.
121
122#### Simple YAML
123The fixture will also process YAML files inside of a fixture. If the fixture contains a file with the name `obj.yaml` then it will load the fixture with this YAML file, parse it, performan template replacement, and save it within the fixture in an exposed filed named `fixture.yamlObj`. Note that `fixture.obj` is the legacy name for JSON objects and NOT YAML. The fixture will also contain a property named `fixture.yaml` which is a string representing the contents of the YAML file.
124
125
126#### Fixture with Template Replacement
127```javascript
128const Fixture = require('util.fixture');
129
130let fixture = Fixture('test-fixture', {
131 jsonFile: 'test-directory/somefile.json',
132 dataFile: 'test-file.txt',
133 templateData: {
134 replaceMe: 'test data'
135 }
136});
137
138... // your test
139```
140
141Loads a fixture and then searches through all of the files in that fixture for template replacement values. The same replacements are applied to all files. This example also demonstrates the use of optional parameters to change the names of the JSON file and data file name. An example directory structure for `test-fixture` within the temporary location would be:
142
143```
144./__tests__/fixtures/test-fixture/
145 test-directory/
146 somefile.json
147 test-file.txt
148```
149
150The constructor would copy the fixture to the temporary location, perform template replacement on each of the files (ignoring directories), save them to their temporary versions, and then parse the `jsonFile` and `dataFile` parameters into `fixture.obj` and `fixture.data`. The example text file above named `test-file.txt` would be:
151
152```
153Test information
154
155{replaceMe}
156```
157
158And after replacement would be:
159
160```
161Test information
162
163test data
164```
165
166This sample file would also be [parsed as a file list](https://www.npmjs.com/package/util.filelist). This would read each line from the file (ignoring blank lines and # comments) and save each line into an array named `fixture.data`. This is a way to take a large list of data, parse it, and store it into an array.
167
168
169#### Empty Fixture (Temporary Directory)
170```javascript
171const Fixture = require('util.fixture');
172
173let fixture = Fixture('tmpdir');
174
175... // your test
176```
177
178A fixture with the name `tmpdir` is a special case. This will create a temporary directory, but will have no files or directories within it. It will not perform any template replacements. The directory is accessible through `fixture.dir`.
179
180A temporary directory can also be created with an empty constructor:
181
182```javascript
183const Fixture = require('util.fixture');
184
185let fixture = Fixture();
186
187... // your test
188```
189
190When the fixture is cleaned up this directory would be removed.
191
192
193#### Pattern Generator
194When the fixture is instatiated a test pattern string is created. This string represents a 2D array of repeated chevrons. Note that this data is stored as a newline delimited string.
195
196The default pattern is a set of chevrons of the letters `a` - `z`, where each letter is a row of 80 columns. e.g.
197
198```
199aaaaaaaaaa...
200bbbbbbbbbb...
201cccccccccc...
202...
203zzzzzzzzzz...
204```
205
206To create a default pattern use:
207
208```javascript
209const fixture = new Fixture('pattern');
210fixture.pattern; // a string representing the pattern.
211```
212
213To create a custom pattern use:
214
215```javascript
216const fixture = new Fixture('pattern', {
217 pattern: {
218 chevrons: ['0', '1', '2'],
219 columns: 80,
220 repeat: 5
221 }
222});
223```
224This will create a 15 x 80 grid of numbers, where each row is `0, 1, 2, ...` (15 rows).
225
226
227#### LoremIpsum Generator
228When the `Fixture` is instantiated it will generate a [lorem ipsum](https://en.wikipedia.org/wiki/Lorem_ipsum) string (random text). It uses the [lorem-ipsum](https://github.com/knicklabs/lorem-ipsum.js) package to create the text. The text is accessed through the property `.loremIpsum`. See the package link above for configuration options. A special name `loremIpsum` can be used if no fixture directory structure is required.
229
230```javascript
231const fixture = new Fixture('loremIpsum', {
232 loremIpsum: {
233 count: 3,
234 sentenceLowerBound: 10,
235 sentenceUpperBound: 20
236 }
237});
238
239fixture.loremIpsum;
240```
241
242The fixture above generates a custom lorem ipsum string with 3 sentences where each sentence has between 10 and 20 words.
243
244
245#### Script Execution
246If the fixture contains a script named `fixture.js` then this script will be executed when the fixture is instantiated. Note that this script will only work with built in node *requires* unless the fixture itself contains its own `node_modules` directory. The script is removed from the temporary fixture after it is executed.
247
248The name of the execution script can be changed as an optional parameter to the fixture construtor:
249
250```javascript
251const Fixture = require('util.fixture');
252
253let fixture = Fixture('test-fixture', {
254 script: 'ascript.js'
255});
256
257... // your test
258```
259
260
261#### Cleanup
262When all tests are complete the fixture should be cleaned up. The class contains a static method named `cleanup`. In [ava] this is used in the `test.after.always` hook:
263
264```javascript
265test.after.always.cb(t => {
266 Fixture.cleanup((err: Error, directories: string[]) => {
267 if (err) {
268 t.fail(err.message);
269 }
270
271 directories.forEach((directory: string) => {
272 t.false(fs.existsSync(directory));
273 });
274
275 t.end();
276 });
277});
278```
279
280The cleanup function only needs to be called once per testing file. The class keeps track of all test directories that were created when the Fixture is instantiated and removes them when the cleanup is called.
281
282Note that when using [ava] the hook `test.after.always` is executed within each separate test file and NOT once per overall test execution. This code needs to be part of each test file to clean up the files related to those tests. If this is not executed, then all of the temp files created by the fixture will remain (and you will be required to clean them up). Note that this process has intermittent issues on Windows. It seems that the [fs-extra remove function](https://github.com/jprichardson/node-fs-extra/blob/master/docs/remove.md) will occasionally fail when trying to delete files if Windows still has a process attached to it (like file explorer).
283
284
285See [tests.js](https://github.com/jmquigley/util.fixture/blob/master/test/tests.ts) in this repository for examples of these usage patterns.
286
287The module also contains a helper function named `cleanup()` which handles the process listed above. The example below works with [jest](https://jestjs.io/) testing.
288
289```javascript
290import {cleanup, Fixture} from "util.fixture";
291
292afterall((done) => {
293 cleanup({done, message: "done with fixture testing"});
294});
295```
296
297
298## API
299
300### Fixture([{name}, opts])
301
302This is a single constructor exposed by the module.
303
304##### parameters
305
306- `name {string}`: The name of the fixture to use. This corresponds to an entry in `./__tests__/fixtures/{name}`. When this is empty an empty, temporary directory is created.
307- `opts: {object}`: optional parameters (listed below)
308
309##### options
310
311The following options can be used to customize the fixture. They can be set as an optional object given to the class during instantiation or within `package.json` in a section named *fixture*. The precedence of application, from lowest to highest, is the default internal options, the `package.json`, and finally the constructor options.
312
313- `basedir {string}`: The base location where the fixture will be temporarily located. The default location is determined by the environment variable `TMP` first or `TEMP` if TMP is not found. If neither of these are set, then `~/.tmp/unit-test-data` is created and used within the users home directory. This must be a directory that is writable by the user running the test.
314- `dataFile {string}`: The name of the data list file, within the fixture location, that will be parsed and saved into `fixture.data` as an array of lines. By default this file is `data.list`. It is parsed by the [util.filelist](https://www.npmjs.com/package/util.filelist) module. This is a way to get a large list of information into the fixture.
315- `fixtureDirectory {string}`: The location within the project where fixtures are found. The default is `./__tests__/fixtures`.
316- `jsonFile {string}`: The name of a JSON data file that will be parsed and saved into `fixture.obj` and `fixture.jsonObj`. By default this file is named `obj.json` within the fixture.
317- `pattern {object}` - configuration overrides for the pattern generator. It contains 3 attributes: columns, repeat count, and an array of chevrons.
318- `loremIpsum {object}`: [configuration options](https://github.com/knicklabs/lorem-ipsum.js/blob/master/README.md) for the lorem ipsum generator.
319- `script {string}`: The name of the node script that will be executed when the fixture is instantiated. The default nam is `fixture.js`.
320- `templateData {object}`: a map of key/value pairs that are used for replacement within each fixture file. The [string-template](https://www.npmjs.com/package/string-template) library is used to perform the replacement. All files are checked.
321- `yamlFile {string}`: The name of a YAML data file that will be parsed and saved into `fixture.yamlObj`. By default this file is named `obj.yaml` within the fixture.
322
323
324##### attributes
325Instantiation of the class returns an object with the following attributes:
326
327- `.basedir` - the root temporary directory for all tests. This can be changed as an option to the function constructor
328- `.cleanup()` - static method on the class that removes the base directory and and all artifacts copied there. Generally this would be used at the end of ALL testing. In [ava] this would be done in the `test.after.always` function.
329- `.data` - if the fixture contains a file named `data.list` or a text file named by the `dataFile` option, then it will be processed and placed here. This is an Array object when defined.
330- `.dir` - the location of the temporary directory created for this fixture.
331- `.files` - an array of files that were found within the fixture and placed into the temporary `.dir`.
332- `.json` - a text string representing a JSON file that was read into the fixture. It's an empty string if there was no JSON file to process.
333- `.jsonObj` - if the fixture contains `obj.json` or a JSON file named by the `jsonFile` option, then it is parsed and the contents of that JSON are stored here. The JSON file will go through template replacement before it is parsed. There is also an alias named `.obj`.
334- `.name` - the name of the requested fixture. This is the first string parameter to the Fixture constructor.
335- `.loremIpsum` - a string containing a randomly generated lorem ipsum string/sentence.
336- `.pattern` - a string contained a MxN repeating pattern.
337- `.read({filename}): string` - Reads the contents of one of the files within the fixture and returns it as a string. The filename is a relative path within the fixture (the absolute path is resolved by the class). This is the contents of the file after it has been processed through template replacement.
338- `.src` - the absolute directory path for the fixture files.
339- `.toString()` - returns a string that shows the internal representation of the fixture. It will show all of these attributes and the options that were passed to the class when it was instantiated.
340- `.yaml` - a text string representing a YAML file that was read into the fixture. It's an empty string if there was no YAML file to process.
341- `.yamlObj` - if the fixture contains `obj.yaml` or a YAML file named by the `yamlFile` option, then it is parsed and the contents of that YAML are stored here. The YAML file will go through template replacement before it is parsed.
342
343##### events
344
345- `loaded` - This event fires once the constructor has finished creating the fixture.
346
347## Template Data Variables
348The following are template variables that automatically added to the variable expansion list (templateData).
349
350- `DIR`: the location where the fixture will be copied. e.g.
351
352```
353{
354 file: "{DIR}/somefile.txt"
355}
356```
357
358[ava]: https://github.com/avajs/ava