Skip to content

Commit

Permalink
feat(fixtures): add fixtures directory (#6)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `fixtures` has been repurposed. See the docs.
  • Loading branch information
Kent C. Dodds committed May 19, 2017
1 parent c157316 commit 6e1554d
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 45 deletions.
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,29 @@ to provide this option.
This can be used to specify a title for the describe block (rather than using
the `pluginName`).

#### filename

Relative paths from the other options will be relative to this. Normally you'll
provide this as `filename: __filename`. The only `options` property affected by
this value is `fixtures`. Test Object properties affected by this value are:
`fixture` and `outputFixture`. If those properties are not
absolute paths, then they will be `path.join`ed with `path.dirname` of the
`filename`.

#### fixtures

This is used in combination with the test object's `fixture` and `outputFixture`
options. This is used as the base directory with which to resolve relative
paths for those options.
This is a path to a directory with this format:

Note: you really only need to specify this option if one of your test objects
uses `fixture` or `outputFixture` without absolute paths.
```
fixtures
├── first-test # test title will be: "first test"
│   ├── code.js # required
│   └── output.js # required
└── second-test
├── .babelrc # optional
├── code.js
└── output.js
```

#### tests

Expand Down
2 changes: 1 addition & 1 deletion package-scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = {
description: 'delete the dist directory and run babel to build the files',
script: series(
rimraf('dist'),
'babel --copy-files --out-dir dist --ignore *.test.js,__fixtures__ src'
'babel --copy-files --out-dir dist --ignore "**/__tests__/**" src'
),
},
lint: {
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"common-tags": "^1.4.0",
"invariant": "^2.2.2",
"lodash.merge": "^4.6.0",
"path-exists": "^3.0.0",
"recast": "^0.12.3",
"strip-indent": "^2.0.0"
},
Expand Down Expand Up @@ -65,7 +66,8 @@
"testEnvironment": "node",
"testPathIgnorePatterns": [
"/node_modules/",
"__fixtures__"
"__fixtures__",
"__helpers__"
],
"coverageThreshold": {
"global": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello');
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello');
File renamed without changes.
5 changes: 5 additions & 0 deletions src/__tests__/__fixtures__/fixtures/changed/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": [
"../../../__helpers__/identifier-reverse-plugin.js"
]
}
1 change: 1 addition & 0 deletions src/__tests__/__fixtures__/fixtures/changed/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('hello');
1 change: 1 addition & 0 deletions src/__tests__/__fixtures__/fixtures/changed/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
elosnoc.gol('hello');
1 change: 1 addition & 0 deletions src/__tests__/__fixtures__/fixtures/unchanged/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'use strict';
1 change: 1 addition & 0 deletions src/__tests__/__fixtures__/fixtures/unchanged/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'use strict';
File renamed without changes.
11 changes: 11 additions & 0 deletions src/__tests__/__helpers__/identifier-reverse-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = identifierReversePlugin

function identifierReversePlugin() {
return {
visitor: {
Identifier(idPath) {
idPath.node.name = idPath.node.name.split('').reverse().join('')
},
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ var ih = \\"hey\\";
"
`;

exports[`can fail tests in fixtures at an absolute path 1`] = `"actual output does not match output.js"`;

exports[`default will throw if output changes 1`] = `"Expected output to not change, but it did"`;

exports[`plugin is required 1`] = `"plugin is a required parameter."`;
Expand All @@ -20,11 +22,9 @@ exports[`throws an invariant if the plugin name is not inferable 1`] = `"The \`p

exports[`throws error if fixture provided and code changes 1`] = `"Expected output to not change, but it did"`;

exports[`throws error if there is a problem parsing 1`] = `"unknown: Unexpected token (1:0)"`;

exports[`throws error when error expected but no error thrown 1`] = `"Expected to throw error, but it did not."`;

exports[`throws error when function doesnt return true 1`] = `"test message"`;
exports[`throws error when function doesn't return true 1`] = `"test message"`;

exports[`throws if output is incorrect 1`] = `"Output is incorrect."`;

Expand Down
72 changes: 49 additions & 23 deletions src/index.test.js → src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import fs from 'fs'
import path from 'path'
import assert from 'assert'
// eslint-disable-next-line import/default
import pluginTester from './'
import pluginTester from '../'
import identifierReversePlugin from './__helpers__/identifier-reverse-plugin'

let errorSpy, describeSpy, itSpy, itOnlySpy, equalSpy

Expand Down Expand Up @@ -116,7 +117,7 @@ test('applies modifier if one is specified', () => {
test('default will throw if output changes', () => {
const tests = ['var hello = "hi";']
expect(() =>
pluginTester(getOptions({plugin: IdentifierReversePlugin, tests})),
pluginTester(getOptions({plugin: identifierReversePlugin, tests})),
).toThrowErrorMatchingSnapshot()
})

Expand Down Expand Up @@ -193,20 +194,19 @@ test('can pass with fixture and outputFixture', () => {
test('throws error if fixture provided and code changes', () => {
const tests = [{fixture: getFixturePath('fixture1.js')}]
expect(() =>
pluginTester(getOptions({plugin: IdentifierReversePlugin, tests})),
pluginTester(getOptions({plugin: identifierReversePlugin, tests})),
).toThrowErrorMatchingSnapshot()
})

test('can resolve a fixture with the fixtures option', () => {
const fixtures = getFixturePath()
test('can resolve a fixture with the filename option', () => {
const tests = [
{
fixture: 'fixture1.js',
outputFixture: 'outure1.js',
fixture: '__fixtures__/fixture1.js',
outputFixture: '__fixtures__/outure1.js',
},
]
try {
pluginTester(getOptions({fixtures, tests}))
pluginTester(getOptions({filename: __filename, tests}))
} catch (error) {
const actual = getFixtureContents('fixture1.js')
const expected = getFixtureContents('outure1.js')
Expand All @@ -219,6 +219,34 @@ test('can resolve a fixture with the fixtures option', () => {
}
})

test('can pass tests in fixtures relative to the filename', () => {
pluginTester(
getOptions({
filename: __filename,
fixtures: '__fixtures__/fixtures',
tests: null,
}),
)
expect(describeSpy).toHaveBeenCalledTimes(1)
expect(itSpy).toHaveBeenCalledTimes(2)
expect(itSpy.mock.calls).toEqual([
[`changed`, expect.any(Function)],
[`unchanged`, expect.any(Function)],
])
})

test('can fail tests in fixtures at an absolute path', () => {
expect(() =>
pluginTester(
getOptions({
plugin: identifierReversePlugin,
tests: null,
fixtures: getFixturePath('failing-fixtures'),
}),
),
).toThrowErrorMatchingSnapshot()
})

test('throws invariant if snapshot and output are both provided', () => {
const tests = [{code: simpleTest, output: 'anything', snapshot: true}]
expect(() =>
Expand Down Expand Up @@ -247,7 +275,7 @@ test('takes a snapshot', () => {
// work pretty well soooooo... 😀
const tests = [simpleTest]
pluginTester(
getOptions({snapshot: true, tests, plugin: IdentifierReversePlugin}),
getOptions({snapshot: true, tests, plugin: identifierReversePlugin}),
)
})

Expand Down Expand Up @@ -309,7 +337,7 @@ test('can capture errors with function', () => {
testError(err => /mess/.test(err.message) && err instanceof SyntaxError)
})

test('throws error when function doesnt return true', () => {
test(`throws error when function doesn't return true`, () => {
expect(() => testError(() => false)).toThrowErrorMatchingSnapshot()
})

Expand All @@ -322,9 +350,17 @@ test('throws error when error expected but no error thrown', () => {
})

test('throws error if there is a problem parsing', () => {
expect(() => {
pluginTester(getOptions({tests: [`][fkfhgo]fo{r`]}))
}).toThrowErrorMatchingSnapshot()
try {
pluginTester(
getOptions({
tests: [`][fkfhgo]fo{r`],
babelOptions: {filename: __filename},
}),
)
} catch (error) {
expect(error.constructor).toBe(SyntaxError)
expect(error.message.endsWith('Unexpected token (1:0)'))
}
})

function getOptions(overrides) {
Expand All @@ -344,13 +380,3 @@ function getFixtureContents(fixture) {
function getFixturePath(fixture = '') {
return path.join(__dirname, '__fixtures__', fixture)
}

function IdentifierReversePlugin() {
return {
visitor: {
Identifier(idPath) {
idPath.node.name = idPath.node.name.split('').reverse().join('')
},
},
}
}
82 changes: 70 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from 'assert'
import path from 'path'
import fs from 'fs'
import pathExists from 'path-exists'
import merge from 'lodash.merge'
import invariant from 'invariant'
import * as recast from 'recast'
Expand All @@ -11,9 +12,11 @@ import {oneLine} from 'common-tags'
module.exports = pluginTester

const fullDefaultConfig = {
parserOpts: {parser: recast.parse},
generatorOpts: {generator: recast.print, lineTerminator: '\n'},
babelrc: false,
babelOptions: {
parserOpts: {parser: recast.parse},
generatorOpts: {generator: recast.print, lineTerminator: '\n'},
babelrc: false,
},
}

function pluginTester(
Expand All @@ -23,9 +26,20 @@ function pluginTester(
title: describeBlockTitle = pluginName,
tests,
fixtures,
filename,
...rest
} = {},
) {
if (fixtures) {
testFixtures({
plugin,
pluginName,
title: describeBlockTitle,
fixtures,
filename,
...rest,
})
}
const testAsArray = toTestArray(tests)
if (!testAsArray.length) {
return
Expand All @@ -48,7 +62,7 @@ function pluginTester(
} = merge(
{},
testerConfig,
toTestConfig({testConfig, index, plugin, pluginName, fixtures}),
toTestConfig({testConfig, index, plugin, pluginName, filename}),
)

if (modifier) {
Expand Down Expand Up @@ -131,6 +145,47 @@ function pluginTester(
})
}

function testFixtures({
plugin,
title: describeBlockTitle,
fixtures,
filename,
...rest
}) {
describe(`${describeBlockTitle} fixtures`, () => {
const fixturesDir = getPath(filename, fixtures)
fs.readdirSync(fixturesDir).forEach(caseName => {
it(caseName.split('-').join(' '), () => {
const fixtureDir = path.join(fixturesDir, caseName)
const codePath = path.join(fixtureDir, 'code.js')
const babelRcPath = path.join(fixtureDir, '.babelrc')

const {babelOptions} = merge(
fullDefaultConfig,
{
babelOptions: {
plugins: [plugin],
// if they have a babelrc, then we'll let them use that
// otherwise, we'll just use our simple config
babelrc: pathExists.sync(babelRcPath),
},
},
rest,
)
const actual = babel
.transformFileSync(codePath, babelOptions)
.code.trim()

const output = fs
.readFileSync(path.join(fixtureDir, 'output.js'), 'utf8')
.trim()

assert.equal(output, actual, 'actual output does not match output.js')
})
})
})
}

function toTestArray(tests) {
tests = tests || [] // null/0/false are ok, so no default param
if (Array.isArray(tests)) {
Expand All @@ -149,16 +204,16 @@ function toTestArray(tests) {
}, [])
}

function toTestConfig({testConfig, index, plugin, pluginName, fixtures}) {
function toTestConfig({testConfig, index, plugin, pluginName, filename}) {
if (typeof testConfig === 'string') {
testConfig = {code: testConfig}
}
const {
title,
fixture,
code = getCode(fixtures, fixture),
code = getCode(filename, fixture),
fullTitle = `${index + 1}. ${title || pluginName}`,
output = getCode(fixtures, testConfig.outputFixture),
output = getCode(filename, testConfig.outputFixture),
} = testConfig
return merge({}, testConfig, {
babelOptions: {plugins: [plugin]},
Expand All @@ -168,15 +223,18 @@ function toTestConfig({testConfig, index, plugin, pluginName, fixtures}) {
})
}

function getCode(fixtures, fixture) {
function getCode(filename, fixture) {
if (!fixture) {
return ''
}
let fullPath = fixture
if (!path.isAbsolute(fixture)) {
fullPath = path.join(fixtures, fixture)
return fs.readFileSync(getPath(filename, fixture), 'utf8')
}

function getPath(filename, basename) {
if (path.isAbsolute(basename)) {
return basename
}
return fs.readFileSync(fullPath, 'utf8')
return path.join(path.dirname(filename), basename)
}

function requiredParam(name) {
Expand Down

0 comments on commit 6e1554d

Please sign in to comment.