-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add jest matcher #150
Merged
Merged
Add jest matcher #150
Changes from 6 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
4f3fc3b
Add jest matcher
a-xin 4ba2c43
Clean-up jest plugin
a-xin 47a92c5
Add tests for jest plugin
a-xin d417e7c
Add docs for jest plugin
a-xin 170e1a7
Add run script for jest plugin tests
a-xin e9db456
Run jest plugin tests on travis
a-xin 396bd0d
Use grep to check node version
a-xin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
|
||
## Using Targaryen with Jest | ||
|
||
1. Run `npm install -g jest` and `npm install --save-dev targaryen`. | ||
|
||
2. Create a new directory for your security tests (**NOTE**: Jest defaults to look for tests inside of `__tests__` folders, or in files that end in `.spec.js` or `.test.js`). | ||
|
||
3. Add a new *fixture JSON* for the state of your Firebase. Call this `spec/security/<firebase path>.json`. This file will describe the state of the Firebase data store for your tests, that is, what you can get via the `root` and `data` variables in the security rules. | ||
|
||
4. Create a new file for your first set of tests, like `spec/security/<firebase path>.spec.js`. | ||
|
||
5. Add the following content to the top of the new file: | ||
|
||
```js | ||
// user-rules.spec.js | ||
const targaryen = require('targaryen/plugins/jest'); | ||
|
||
expect.extend({ | ||
toAllowRead: targaryen.toAllowRead, | ||
toAllowUpdate: targaryen.toAllowUpdate, | ||
toAllowWrite: targaryen.toAllowWrite, | ||
}); | ||
|
||
const RULES_PATH = 'database.rules.json'; | ||
const rules = targaryen.json.loadSync(RULES_PATH); | ||
const initialData = require(path.join(__dirname, path.basename(__filename, '.spec.js') + '.json')); | ||
|
||
test('basic', () => { | ||
const database = targaryen.getDatabase(rules, initialData); | ||
|
||
expect(database.as(targaryen.users.unauthenticated)).not.toAllowRead('/user'); | ||
expect(database.as(targaryen.users.unauthenticated)).toAllowRead('/public'); | ||
expect(database.as(targaryen.users.facebook)).toAllowRead('/user'); | ||
expect(database.as({ uid: '1234'})).toAllowWrite('/user/1234', { | ||
name: 'Anna', | ||
}); | ||
}); | ||
``` | ||
|
||
where `RULES_PATH` is the path to your security rules JSON file. If your security rules are broken, Targaryen will throw an exception at this point with detailed information about what specifically is broken. | ||
|
||
6. Write your security tests. | ||
|
||
The subject of every assertion will be the authentication state (i.e., `auth`) of the user trying the operation, so for instance, `null` would be an unauthenticated user, or a Firebase Password Login user would look like `{ uid: 'password:500f6e96-92c6-4f60-ad5d-207253aee4d3', id: 1, provider: 'password' }`. There are symbolic versions of these in `targaryen.users`. | ||
|
||
See the API section below for details, or take a look at the example files here. | ||
|
||
7. Run the tests with `jest`. | ||
|
||
## API | ||
|
||
- import with `require('targaryen/plugins/jest')`. | ||
- `jestTargaryen.toAllowRead`, `jestTargaryen.toAllowWrite`, `jestTargaryen.toAllowUpdate`, `jestTargaryen.toBeAllowed`: The jest matchers. Load them using `expect.extend({toAllowRead: targaryen.toAllowRead, toAllowWrite: targaryen.toAllowWrite, toAllowUpdate: targaryen.toAllowUpdate, toBeAllowed: targaryen.toBeAllowed});` before running any tests. | ||
- `jestTargaryen.getDatabase(rules: object|Ruleset, data: object|DataNode, now: null|number): Database`: Wrapper for `targaryen.database()`. | ||
- `jestTargaryen.getDebugDatabase(rules: object|Ruleset, data: object|DataNode, now: null|number): Database`: Wrapper for `targaryen.database()` that also enables debug mode. Use this if you write your tests using the generic matcher `toBeAllowed()`. | ||
- `jestTargaryen.json`: Export of `firebase-json` to allow parsing of firebase rule files. | ||
- `jestTargaryen.users`: A set of authentication objects you can use as the subject of the assertions. Has the following keys: | ||
- `unauthenticated`: an unauthenticated user, i.e., `auth === null`. | ||
- `anonymous`: a user authenticated using Firebase anonymous sessions. | ||
- `password`: a user authenticated using Firebase Password Login. | ||
- `facebook`: a user authenticated by their Facebook account. | ||
- `twitter`: a user authenticated by their Twitter account. | ||
- `google`: a user authenticated by their Google account. | ||
- `github`: a user authenticated by their Github account. | ||
- `expect(auth).canRead(path: string [, options: {now?: number, query?: object} ])`: asserts that the given path is readable by a user with the given authentication data. | ||
- `expect(auth).cannotRead(path: string[, options: {now?: number, query?: object} ])`: asserts that the given path is not readable by a user with the given authentication data. | ||
- `expect(auth).canWrite(path: string [, data: any [, options: {now: number, priority: any} ]])`: asserts that the given path is writable by a user with the given authentication data. Optionally takes a Javascript object containing `newData`, otherwise this will be set to `null`. | ||
- `expect(auth).cannotWrite(path: string [, data: any [, options: {now: number, priority: any} ]])`: asserts that the given path is not writable by a user with the given authentication data. Optionally takes a Javascript object containing `newData`, otherwise this will be set to `null`. | ||
- `expect(auth).canPatch(path: string, patch: {[path: string]: any} [, options: {now: number} ])`: asserts that the given patch (or multi-location update) operation is writable by a user with the given authentication data. | ||
- `expect(auth).cannotPatch(path: string, patch: {[path: string]: any} [, options: {now: number} ])`: asserts that the given patch (or multi-location update) operation is writable by a user with the given authentication data. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,7 @@ | |
"prepare": "npm run build", | ||
"test": "npm run build && mocha -r test/setup.js test/spec/ --recursive && jasmine && node ./bin/targaryen --verbose test/integration/rules.json test/integration/tests.json", | ||
"test:unit": "mocha -r test/setup.js test/spec/ --recursive", | ||
"test:plugin:jest": "jest test/jest", | ||
"test:inspect": "node --inspect --debug-brk node_modules/.bin/_mocha -r test/setup.js test/spec/ --recursive" | ||
}, | ||
"author": "Harry Schmidt <[email protected]>", | ||
|
@@ -72,9 +73,11 @@ | |
"coveralls": "^2.11.15", | ||
"eslint": "^3.9.1", | ||
"eslint-config-xo": "^0.17.0", | ||
"eslint-plugin-jest": "^21.15.1", | ||
"eslint-plugin-node": "^2.1.3", | ||
"istanbul": "^0.4.5", | ||
"jasmine": "^2.1.1", | ||
"jest": "^22.4.3", | ||
"mocha": "^2.1.0", | ||
"sinon": "^1.17.6", | ||
"sinon-chai": "^2.8.0" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/** | ||
* targaryen/plugins/jest - Reference implementation of a jest plugin for | ||
* targaryen. | ||
* | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const json = require('firebase-json'); | ||
const targaryen = require('../'); | ||
|
||
// Need to disable eslint rule for jest's utils: this.utils.EXPECTED_COLOR('a') | ||
/* eslint-disable new-cap */ | ||
|
||
function toBeAllowed(result) { | ||
const pass = result.allowed === true; | ||
const message = pass ? | ||
() => `Expected operation to be ${this.utils.EXPECTED_COLOR('denied')} but it was ${this.utils.RECEIVED_COLOR('allowed')}\n\n${result.info}` : | ||
() => `Expected operation to be ${this.utils.EXPECTED_COLOR('allowed')} but it was ${this.utils.RECEIVED_COLOR('denied')}\n\n${result.info}`; | ||
|
||
return { | ||
message, | ||
pass | ||
}; | ||
} | ||
|
||
function toAllowRead(database, path, options) { | ||
const result = database | ||
.with({debug: true}) | ||
.read(path, options); | ||
|
||
const pass = result.allowed === true; | ||
const message = pass ? | ||
() => `Expected ${this.utils.EXPECTED_COLOR('read')} to be ${this.utils.EXPECTED_COLOR('denied')} but it was ${this.utils.RECEIVED_COLOR('allowed')}\n\n${result.info}` : | ||
() => `Expected ${this.utils.EXPECTED_COLOR('read')} to be ${this.utils.EXPECTED_COLOR('allowed')} but it was ${this.utils.RECEIVED_COLOR('denied')}\n\n${result.info}`; | ||
|
||
return { | ||
message, | ||
pass | ||
}; | ||
} | ||
|
||
function toAllowWrite(database, path, value, options) { | ||
const result = database | ||
.with({debug: true}) | ||
.write(path, value, options); | ||
|
||
const pass = result.allowed === true; | ||
const message = pass ? | ||
() => `Expected ${this.utils.EXPECTED_COLOR('write')} to be ${this.utils.EXPECTED_COLOR('denied')} but it was ${this.utils.RECEIVED_COLOR('allowed')}\n\n${result.info}` : | ||
() => `Expected ${this.utils.EXPECTED_COLOR('write')} to be ${this.utils.EXPECTED_COLOR('allowed')} but it was ${this.utils.RECEIVED_COLOR('denied')}\n\n${result.info}`; | ||
|
||
return { | ||
message, | ||
pass | ||
}; | ||
} | ||
|
||
function toAllowUpdate(database, path, patch, options) { | ||
const result = database | ||
.with({debug: true}) | ||
.update(path, patch, options); | ||
|
||
const pass = result.allowed === true; | ||
const message = pass ? | ||
() => `Expected ${this.utils.EXPECTED_COLOR('update')} to be ${this.utils.EXPECTED_COLOR('denied')} but it was ${this.utils.RECEIVED_COLOR('allowed')}\n\n${result.info}` : | ||
() => `Expected ${this.utils.EXPECTED_COLOR('update')} to be ${this.utils.EXPECTED_COLOR('allowed')} but it was ${this.utils.RECEIVED_COLOR('denied')}\n\n${result.info}`; | ||
|
||
return { | ||
message, | ||
pass | ||
}; | ||
} | ||
|
||
/** | ||
* Expose `targaryen.database()` for conveniently creating a | ||
* database for a jest test. | ||
* | ||
* @return {Database} | ||
*/ | ||
const getDatabase = targaryen.database; | ||
|
||
/** | ||
* Simple wrapper for `targaryen.database()` that also enables debug mode for | ||
* detailed error messages. | ||
* | ||
* @return {Database} | ||
*/ | ||
function getDebugDatabase() { | ||
return targaryen.database.apply(this, arguments).with({debug: true}); | ||
} | ||
|
||
const jestTargaryen = { | ||
toBeAllowed, | ||
toAllowRead, | ||
toAllowWrite, | ||
toAllowUpdate, | ||
|
||
// NOTE: Exported for convenience only | ||
getDatabase, | ||
getDebugDatabase, | ||
json, | ||
users: targaryen.util.users | ||
}; | ||
|
||
module.exports = jestTargaryen; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
extends: '../../.eslintrc.yml' | ||
plugins: | ||
- jest | ||
env: | ||
jest/globals: true | ||
rules: | ||
jest/no-disabled-tests: warn | ||
jest/no-focused-tests: error | ||
jest/no-identical-title: error | ||
jest/valid-expect: error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`generic matchers toBeAllowed 1`] = ` | ||
"Expected operation to be [32mallowed[39m but it was [31mdenied[39m | ||
Attempt to read /user as null. | ||
/user: read <no rules> | ||
No .read rule allowed the operation. | ||
read was denied." | ||
`; | ||
exports[`generic matchers toBeAllowed 2`] = ` | ||
"Expected operation to be [32mdenied[39m but it was [31mallowed[39m | ||
Attempt to write /user/1234 as {\\"uid\\":\\"1234\\"}. | ||
New Value: \\"{ | ||
\\"name\\": \\"Anna\\" | ||
}\\". | ||
/user/1234: write \\"auth.uid === $uid\\" => true | ||
auth.uid === $uid [=> true] | ||
using [ | ||
$uid = \\"1234\\" | ||
auth = {\\"uid\\":\\"1234\\"} | ||
auth.uid = \\"1234\\" | ||
] | ||
write was allowed." | ||
`; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please change the version test. Either use
grep
or change the previous test to usecut
.cut
is probably better.