@@ -22,11 +22,13 @@ Utilities for testing babel plugins
2222
2323## The problem
2424
25-
25+ You're writing a babel plugin and want to write tests for it.
2626
2727## This solution
2828
29-
29+ This is a fairly simple abstraction to help you write tests for your babel
30+ plugin. It works with ` jest ` (my personal favorite) and most of it should also
31+ work with ` mocha ` and ` jasmine ` .
3032
3133## Installation
3234
@@ -39,11 +41,233 @@ npm install --save-dev babel-plugin-tester
3941
4042## Usage
4143
44+ ### import
45+
46+ ``` javascript
47+ import pluginTester from ' babel-plugin-tester'
48+ // or
49+ const pluginTester = require (' babel-plugin-tester' )
50+ ```
51+
52+ ### Invoke
53+
54+ ``` javascript
55+ import yourPlugin from ' ../your-plugin'
56+
57+ pluginTester ({
58+ plugin: yourPlugin,
59+ tests: [
60+ /* your test objects */
61+ ],
62+ })
63+ ```
64+
65+ ### options
66+
67+ #### plugin
68+
69+ Your babel plugin. For example:
70+
71+ ``` javascript
72+ pluginTester ({
73+ plugin: identifierReversePlugin,
74+ tests: [
75+ /* your test objects */
76+ ],
77+ })
78+
79+ // normally you would import this from your plugin module
80+ function identifierReversePlugin () {
81+ return {
82+ name: ' identifier reverse' ,
83+ visitor: {
84+ Identifier (idPath ) {
85+ idPath .node .name = idPath .node .name .split (' ' ).reverse ().join (' ' )
86+ },
87+ },
88+ }
89+ }
90+ ```
91+
92+ #### pluginName
93+
94+ This is used for the ` describe ` title as well as the test titles. If it
95+ can be inferred from the ` plugin ` 's ` name ` then it will be and you don't need
96+ to provide this option.
97+
98+ #### title
99+
100+ This can be used to specify a title for the describe block (rather than using
101+ the ` pluginName ` ).
102+
103+ #### fixtures
104+
105+ This is used in combination with the test object's ` fixture ` and ` outputFixture `
106+ options. This is used as the base directory with which to resolve relative
107+ paths for those options.
108+
109+ Note: you really only need to specify this option if one of your test objects
110+ uses ` fixture ` or ` outputFixture ` without absolute paths.
111+
112+ #### tests
113+
114+ You provide test objects as the ` tests ` option to ` babel-plugin-tester ` . You can
115+ either provide the ` tests ` as an object of test objects or an array of test
116+ objects.
117+
118+ If you provide the tests as an object, the key will be used as the title of the
119+ test.
120+
121+ If you provide an array, the title will be derived from it's index and a
122+ specified ` title ` property or the ` pluginName ` .
123+
124+ Read more about test objects below.
125+
126+ #### ...rest
42127
128+ The rest of the options you provide will be [ ` lodash.merge ` ] [ lodash-merge ] d
129+ with each test object. Read more about those next!
130+
131+ ### Test Objects
132+
133+ A minimal test object can be:
134+
135+ 1 . A ` string ` representing code
136+ 2 . An ` object ` with a ` code ` property
137+
138+ Here are the available properties if you provide an object:
139+
140+ #### code
141+
142+ The code that you want to run through your babel plugin. This must be provided
143+ unless you provide a ` fixture ` instead. If there's no ` output ` or ` outputFixture `
144+ and ` snapshot ` is not ` true ` , then the assertion is that this code is unchanged
145+ by the plugin.
146+
147+ #### title
148+
149+ If provided, this will be used instead of the ` pluginName ` . If you're using the
150+ object API, then the ` key ` of this object will be the title (see example below).
151+
152+ #### output
153+
154+ If this is provided, the result of the plugin will be compared with this output
155+ for the assertion. It will have any indentation stripped and will be trimmed as
156+ a convenience for template literals.
157+
158+ #### fixture
159+
160+ If you'd rather put your ` code ` in a separate file, you can specify a filename
161+ here. If it's an absolute path, that's the file that will be loaded, otherwise,
162+ this will be ` path.join ` ed with the ` fixtures ` path.
163+
164+ #### outputFixture
165+
166+ If you'd rather put your ` output ` in a separate file, you can specify this
167+ instead (works the same as ` fixture ` ).
168+
169+ #### snapshot
170+
171+ If you'd prefer to take a snapshot of your output rather than compare it to
172+ something you hard-code, then specify ` snapshot: true ` . This will take a
173+ snapshot with both the source code and the output, making the snapshot easier
174+ to understand.
175+
176+ ## Examples
177+
178+ ``` javascript
179+ import pluginTester from ' babel-plugin-tester'
180+ import identifierReversePlugin from ' ../identifier-reverse-plugin'
181+
182+ pluginTester ({
183+ // required
184+ plugin: identifierReversePlugin,
185+
186+ // unnecessary if it's returned with the plugin
187+ pluginName: ' identifier reverse' ,
188+
189+ // defaults to the plugin name
190+ title: ' describe block title' ,
191+
192+ // only necessary if you use fixture or outputFixture in your tests
193+ fixtures: path .join (__dirname , ' __fixtures__' ),
194+
195+ // these will be `lodash.merge`d with the test objects
196+ // below are the defaults:
197+ babelOptions: {
198+ parserOpts: {parser: recast .parse },
199+ generatorOpts: {generator: recast .print , lineTerminator: ' \n ' },
200+ babelrc: false ,
201+ },
202+ snapshot: false , // use jest snapshots (only works with jest)
203+
204+ // tests as objects
205+ tests: {
206+ // the key is the title
207+ // the value is the code that is unchanged (because `snapshot: false`)
208+ // test title will be: `1. does not change code with no identifiers`
209+ ' does not change code with no identifiers' : ' "hello";' ,
210+
211+ // test title will be: `2. changes this code`
212+ ' changes this code' : {
213+ // input to the plugin
214+ code: ' var hello = "hi";' ,
215+ // expected output
216+ output: ' var olleh = "hi";' ,
217+ },
218+ },
219+
220+ // tests as an array
221+ tests: [
222+ // should be unchanged by the plugin (because `snapshot: false`)
223+ // test title will be: `1. identifier reverse`
224+ ' "hello";' ,
225+ {
226+ // test title will be: `2. identifier reverse`
227+ code: ' var hello = "hi";' ,
228+ output: ' var olleh = "hi";' ,
229+ },
230+ {
231+ // test title will be: `3. unchanged code`
232+ title: ' unchanged code' ,
233+ // because this is an absolute path, the `fixtures` above will not be
234+ // used to resolve this path.
235+ fixture: path .join (__dirname , ' some-path' , ' unchanged.js' ),
236+ // no output, outputFixture, or snapshot, so the assertion will be that
237+ // the plugin does not change this code.
238+ },
239+ {
240+ // because these are not absolute paths, they will be joined with the
241+ // `fixtures` path provided above
242+ fixture: ' changed.js' ,
243+ // because outputFixture is provided, the assertion will be that the
244+ // plugin will change the contents of `changed.js` to the contents of
245+ // `changed-output.js`
246+ outputFixture: ' changed-output.js' ,
247+ },
248+ {
249+ // as a convenience, this will have the indentation striped and it will
250+ // be trimmed.
251+ code: `
252+ function sayHi(person) {
253+ return 'Hello ' + person + '!'
254+ }
255+ ` ,
256+ // this will take a jest snapshot. The snapshot will have both the
257+ // source code and the transformed version to make the snapshot file
258+ // easier to understand.
259+ snapshot: true ,
260+ },
261+ ],
262+ })
263+ ```
43264
44265## Inspiration
45266
267+ I've been thinking about this for a while. The API was inspired by:
46268
269+ - ESLint's [ RuleTester] [ RuleTester ]
270+ - [ @thejameskyle ] [ @thejameskyle ] 's [ tweet] [ jamestweet ]
47271
48272## Other Solutions
49273
98322[ twitter-badge ] : https://img.shields.io/twitter/url/https/github.com/kentcdodds/babel-plugin-tester.svg?style=social
99323[ emojis ] : https://github.com/kentcdodds/all-contributors#emoji-key
100324[ all-contributors ] : https://github.com/kentcdodds/all-contributors
325+ [ lodash.merge ] : https://lodash.com/docs/4.17.4#merge
326+ [ RuleTester ] : http://eslint.org/docs/developer-guide/working-with-rules#rule-unit-tests
327+ [ @thejameskyle ] : https://github.com/thejameskyle
328+ [ jamestweet ] : https://twitter.com/thejameskyle/status/864359438819262465
0 commit comments