From 8339813d051d09beabe19938c5c17c3ada1df208 Mon Sep 17 00:00:00 2001 From: Meinaart van Straalen Date: Tue, 16 Oct 2018 16:46:02 +0200 Subject: [PATCH] Add linting, unit tests and `ignoreExtraArrayItems` & Travis integration --- .gitignore | 2 +- .npmignore | 2 + .travis.yml | 15 +++ CHANGELOG.md | 8 ++ README.md | 27 +++-- __tests__/__snapshots__/plugin.test.js.snap | 107 +++++++++++++++++ __tests__/__snapshots__/snapshot.test.js.snap | 16 +++ __tests__/command.test.js | 27 +++++ __tests__/plugin.test.js | 108 +++++++++++++++++ __tests__/snapshot.test.js | 22 ++++ commands.js | 112 ++++++++++-------- config.js | 14 +-- constants.js | 4 +- package.json | 58 ++++++++- plugin-utils.js | 16 +-- plugin.js | 72 ++++++----- save-server.js | 15 ++- snapshot.js | 20 ++-- ui.js | 7 +- 19 files changed, 523 insertions(+), 129 deletions(-) create mode 100644 .travis.yml create mode 100644 __tests__/__snapshots__/plugin.test.js.snap create mode 100644 __tests__/__snapshots__/snapshot.test.js.snap create mode 100644 __tests__/command.test.js create mode 100644 __tests__/plugin.test.js create mode 100644 __tests__/snapshot.test.js diff --git a/.gitignore b/.gitignore index 4d7d84a9..f251266b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ package-lock.json .DS_Store -node_modules +node_modules \ No newline at end of file diff --git a/.npmignore b/.npmignore index 4ef133f1..ee5bbb8a 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1,4 @@ /node_modules/ package-lock.json +.travis.yml +__tests__ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..489766f3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: node_js + +node_js: + - 10.8 +cache: + directories: + - ~/.npm + - ~/.cache +notifications: + email: false +install: + - npm ci +script: + - npm run lint + - npm test \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f005e27c..f9edd023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +1.0.2 / 2018-10-16 +================== + + * Add `ignoreExtraArrayItems` property to configuration + * Add [Travis](https://travis-ci.org/) integration + * Add linter config + * Add Jest unit tests + 1.0.1 / 2018-10-15 ================== diff --git a/README.md b/README.md index 730a3176..c973d2ce 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status][build-badge]][build] + # cypress-plugin-snapshots Plugin for snapshot tests in [Cypress.io](https://www.cypress.io/). @@ -66,16 +68,17 @@ Add the configuration below to your `cypress.json` file to make changes to the d ```javascript "env": { "cypress-plugin-snapshots": { - "autoCleanUp": false, // Automatically remove snapshots that are not used by test - "autopassNewSnapshots": true, // Automatically save & pass new/non-existing snapshots - "diffLines": 3, // How many lines to include in the diff modal - "excludeFields": [], // Array of fieldnames that should be excluded from snapshot - "ignoreExtraFields": false, // Ignore extra fields that are not in `snapshot` - "normalizeJson": true, // Alphabetically sort keys in JSON - "serverEnabled": true, // Enable "update snapshot" server and button in diff modal - "serverHost": "localhost", // Hostname for "update snapshot server" - "serverPort": 2121, // Port number for "update snapshot server" - "updateSnapshots": false, // Automatically update snapshots, useful if you have lots of changes + "autoCleanUp": false, // Automatically remove snapshots that are not used by test + "autopassNewSnapshots": true, // Automatically save & pass new/non-existing snapshots + "diffLines": 3, // How many lines to include in the diff modal + "excludeFields": [], // Array of fieldnames that should be excluded from snapshot + "ignoreExtraArrayItems": false, // Ignore if there are extra array items in result + "ignoreExtraFields": false, // Ignore extra fields that are not in `snapshot` + "normalizeJson": true, // Alphabetically sort keys in JSON + "serverEnabled": true, // Enable "update snapshot" server and button in diff modal + "serverHost": "localhost", // Hostname for "update snapshot server" + "serverPort": 2121, // Port number for "update snapshot server" + "updateSnapshots": false, // Automatically update snapshots, useful if you have lots of changes } } ``` @@ -84,15 +87,15 @@ Add the configuration below to your `cypress.json` file to make changes to the d Below is a list of functionality that is under consideration for implementing in a next version. - Add basic Cypress test for demonstration +- Run basic Cypress test with [Travis](https://travis-ci.org/) based on [Cypress Travis example](https://github.com/cypress-io/cypress-example-kitchensink/blob/master/.travis.yml) - Add screenshots to README - Make `toMatchSnapshot` work for DOM elements - Disable "update snapshots" server in headless mode - Contact Cypress team to be included in [official plugin list on Cypress.io](https://docs.cypress.io/plugins/index.html) -- Add unit tests +- Add more unit tests - Consider moving configuration to `initPlugin`. - Extract CSS and javascript to separate files - Add [JSDoc](http://usejsdoc.org/) documentation -- Add [Travis](https://travis-ci.org/) integration based on [Cypress Travis example](https://github.com/cypress-io/cypress-example-kitchensink/blob/master/.travis.yml) - Investigate code coverage tests with [Coveralls](https://coveralls.io/) and [Istanbul](http://gotwarlost.github.io/istanbul/) - Consider implementing visual snapshots with [jest-image-snapshot](https://github.com/americanexpress/jest-image-snapshot) diff --git a/__tests__/__snapshots__/plugin.test.js.snap b/__tests__/__snapshots__/plugin.test.js.snap new file mode 100644 index 00000000..7b9e5e8b --- /dev/null +++ b/__tests__/__snapshots__/plugin.test.js.snap @@ -0,0 +1,107 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`plugin keepKeysFromExpected ignoreExtraFields: false, ignoreExtraArrayItems: false 1`] = ` +Object { + "extra": "extra", + "field": "value", + "items": Array [ + Object { + "items": Array [ + Object { + "extra": "extra1", + "name": "subitem1", + }, + Object { + "extra": "extra2", + "name": "subitem2", + }, + ], + "name": "item1", + }, + Object { + "name": "item2", + }, + Object { + "name": "item3", + }, + Object { + "name": "item4", + }, + ], +} +`; + +exports[`plugin keepKeysFromExpected ignoreExtraFields: false, ignoreExtraArrayItems: true 1`] = ` +Object { + "extra": "extra", + "field": "value", + "items": Array [ + Object { + "items": Array [ + Object { + "extra": "extra1", + "name": "subitem1", + }, + ], + "name": "item1", + }, + Object { + "name": "item2", + }, + Object { + "name": "item3", + }, + ], +} +`; + +exports[`plugin keepKeysFromExpected ignoreExtraFields: true, ignoreExtraArrayItems: false 1`] = ` +Object { + "field": "value", + "items": Array [ + Object { + "items": Array [ + Object { + "name": "subitem1", + }, + Object { + "extra": "extra2", + "name": "subitem2", + }, + ], + "name": "item1", + }, + Object { + "name": "item2", + }, + Object { + "name": "item3", + }, + Object { + "name": "item4", + }, + ], +} +`; + +exports[`plugin keepKeysFromExpected ignoreExtraFields: true, ignoreExtraArrayItems: true 1`] = ` +Object { + "field": "value", + "items": Array [ + Object { + "items": Array [ + Object { + "name": "subitem1", + }, + ], + "name": "item1", + }, + Object { + "name": "item2", + }, + Object { + "name": "item3", + }, + ], +} +`; diff --git a/__tests__/__snapshots__/snapshot.test.js.snap b/__tests__/__snapshots__/snapshot.test.js.snap new file mode 100644 index 00000000..58afa9fa --- /dev/null +++ b/__tests__/__snapshots__/snapshot.test.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`snapshot subjectToSnapshot normalizes 1`] = ` +Object { + "bar": "foo", + "foo": "bar", + "sub": Object { + "asubsub": Object { + "bar": "foo", + "foo": "bar", + }, + "bar": "foo", + "foo": "bar", + }, +} +`; diff --git a/__tests__/command.test.js b/__tests__/command.test.js new file mode 100644 index 00000000..5ba69c7d --- /dev/null +++ b/__tests__/command.test.js @@ -0,0 +1,27 @@ +const { initCommands } = require('../commands'); + +global.Cypress = { + env: () => {}, + config: () => {}, + Commands: { + add: jest.fn(), + }, +}; + +global.cy = {}; + +describe('command', () => { + it('should create command', () => { + global.before = jest.fn(); + global.after = jest.fn(); + + initCommands(); + global.cy.task = jest.fn().mockResolvedValue({ pass: true }); + + expect(global.Cypress.Commands.add).toBeCalled(); + expect(global.Cypress.Commands.add.mock.calls.length).toEqual(1); + expect(global.Cypress.Commands.add.mock.calls[0][0]).toEqual('toMatchSnapshot'); + expect(global.after).toBeCalled(); + expect(global.before).toBeCalled(); + }); +}); \ No newline at end of file diff --git a/__tests__/plugin.test.js b/__tests__/plugin.test.js new file mode 100644 index 00000000..6286bb7a --- /dev/null +++ b/__tests__/plugin.test.js @@ -0,0 +1,108 @@ +const configModule = require('../config'); + +jest.mock("../config.js"); + +jest.spyOn(configModule, 'initConfig') + .mockImplementation((config) => config); + +global.Cypress = { + env: () => {}, + config: () => {}, + Commands: { + add: jest.fn(), + }, +}; + +global.cy = {}; + +describe('plugin', () => { + it('initPlugin', () => { + const globalConfig = { + env: { + "cypress-plugin-snapshots": { + "serverEnabled": false, + } + } + }; + jest.spyOn(configModule, 'getConfig') + .mockImplementation(() => globalConfig.env['cypress-plugin-snapshots']); + const on = jest.fn(); + + const { initPlugin } = require('../plugin'); + + initPlugin(on, globalConfig); + expect(on).toBeCalledTimes(1); + }); + + describe('keepKeysFromExpected', () => { + const expected = { + field: "value", + items: [ + {name: "item1", "items": [{name: "subitem1"}]}, + {name: "item2"}, + {name: "item3"}, + ] + }; + + const actual = { + field: "value", + extra: "extra", + items: [ + {name: "item1", "items": [{name: "subitem1", extra: 'extra1'}, {name: "subitem2", extra: 'extra2'}]}, + {name: "item2"}, + {name: "item3"}, + {name: "item4"}, + ] + }; + + it('ignoreExtraFields: false, ignoreExtraArrayItems: false', () => { + const config = { + ignoreExtraFields: false, + ignoreExtraArrayItems: false, + }; + jest.spyOn(configModule, 'getConfig').mockImplementation(() => config); + + const { keepKeysFromExpected } = require('../plugin'); + + const result = keepKeysFromExpected(actual, expected); + expect(result).toMatchSnapshot(); + }); + + it('ignoreExtraFields: true, ignoreExtraArrayItems: false', () => { + const config = { + ignoreExtraFields: true, + ignoreExtraArrayItems: false, + }; + jest.spyOn(configModule, 'getConfig').mockImplementation(() => config); + + const { keepKeysFromExpected } = require('../plugin'); + const result = keepKeysFromExpected(actual, expected); + expect(result).toMatchSnapshot(); + }); + + it('ignoreExtraFields: true, ignoreExtraArrayItems: true', () => { + const config = { + ignoreExtraFields: true, + ignoreExtraArrayItems: true, + }; + jest.spyOn(configModule, 'getConfig').mockImplementation(() => config); + const { keepKeysFromExpected } = require('../plugin'); + + const result = keepKeysFromExpected(actual, expected); + expect(result).toMatchSnapshot(); + }); + + it('ignoreExtraFields: false, ignoreExtraArrayItems: true', () => { + const config = { + ignoreExtraFields: false, + ignoreExtraArrayItems: true, + }; + jest.spyOn(configModule, 'getConfig').mockImplementation(() => config); + + const { keepKeysFromExpected } = require('../plugin'); + + const result = keepKeysFromExpected(actual, expected); + expect(result).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file diff --git a/__tests__/snapshot.test.js b/__tests__/snapshot.test.js new file mode 100644 index 00000000..11540501 --- /dev/null +++ b/__tests__/snapshot.test.js @@ -0,0 +1,22 @@ +const { subjectToSnapshot } = require('../snapshot'); + +describe('snapshot', () => { + describe('subjectToSnapshot', () => { + it('normalizes', () => { + const normalized = subjectToSnapshot({ + foo: 'bar', + bar: 'foo', + sub: { + foo: 'bar', + bar: 'foo', + asubsub: { + foo: 'bar', + bar: 'foo', + } + } + }, true); + + expect(normalized).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file diff --git a/commands.js b/commands.js index a334fa66..7d60c90d 100644 --- a/commands.js +++ b/commands.js @@ -1,25 +1,19 @@ +/* globals Cypress, before, after, cy */ +/* eslint-env browser */ const { MATCH, URL_PREFIX } = require('./constants'); const { initUi } = require('./ui'); -const { formatNormalizedJson, getTestTitle, getSnapshotTitle, getSnapshotFilename, snapshotTitleIsUsed } = require('./snapshot'); +const { + formatNormalizedJson, getTestTitle, getSnapshotTitle, getSnapshotFilename, snapshotTitleIsUsed, +} = require('./snapshot'); const { CONFIG_KEY } = require('./config'); const COMMAND_NAME = 'toMatchSnapshot'; -const NO_LOG = {log: false}; +const NO_LOG = { log: false }; function getConfig() { return Cypress.env(CONFIG_KEY); } -// Inject CSS & JavaScript -before(() => { - initUi(); -}); - -// Clean up unused snapshots -after(() => { - cleanUpSnapshots(); -}); - // Removes unused snapshots from snapshot file function cleanUpSnapshots() { const config = getConfig(); @@ -58,55 +52,71 @@ function getTestForTask(test) { return { id: test.id, title: test.title, - parent: test.parent && test.parent.title ? getTestForTask(test.parent): null - } + parent: test.parent && test.parent.title ? getTestForTask(test.parent) : null, + }; } -Cypress.Commands.add(COMMAND_NAME, { prevSubject: 'optional' }, (subject, taskOptions) => { - if (!subject) { - return; - } +function initCommands() { + // Inject CSS & JavaScript + before(() => { + initUi(); + }); - const config = getConfig(); - const options = taskOptions || {}; - options.ignoreExtraFields = config.ignoreExtraFields || options.ignoreExtraFields === true; - - const test = getTestForTask(); - const testTitle = getTestTitle(test); - const snapshotTitle = getSnapshotTitle(test); - let task; - - const toMatchSnapshot = (result) => { - const args = parent.window.btoa(JSON.stringify(result)); - const message = result.passed ? - result.expected ? 'Snapshots match' : 'Snapshot created, autopassed' - : `[compare snapshot](${URL_PREFIX}${args})`; - - const log = Cypress.log({ - $el: subject, - name: COMMAND_NAME, - displayName: 'snapshot', - message, - consoleProps: () => { - return result; - } - }); + // Clean up unused snapshots + after(() => { + cleanUpSnapshots(); + }); - if (!result.passed) { - log.set('state', 'failed'); - throw new Error('Snapshots do not match'); + Cypress.Commands.add(COMMAND_NAME, { prevSubject: 'optional' }, (subject, taskOptions) => { + if (!subject) { + return subject; } - return subject; - }; + const config = getConfig(); + const options = taskOptions || {}; + options.ignoreExtraFields = config.ignoreExtraFields || options.ignoreExtraFields === true; + + const test = getTestForTask(); + const testTitle = getTestTitle(test); + const snapshotTitle = getSnapshotTitle(test); + + const toMatchSnapshot = (result) => { + const args = window.parent.window.btoa(JSON.stringify(result)); + const passedMessage = result.expected ? 'Snapshots match' : 'Snapshot created, autopassed'; + const message = result.passed + ? passedMessage + : `[compare snapshot](${URL_PREFIX}${args})`; + + const log = Cypress.log({ + $el: subject, + name: COMMAND_NAME, + displayName: 'snapshot', + message, + consoleProps: () => result, + }); + + if (!result.passed) { + log.set('state', 'failed'); + throw new Error('Snapshots do not match'); + } - cy.task(MATCH, { + return subject; + }; + + return cy.task(MATCH, { testTitle, testFile: Cypress.spec.absolute, snapshotTitle, subject, - options + options, }, NO_LOG).then(toMatchSnapshot); + }); +} + +module.exports = { + initCommands, +}; - return task; -}); +if (!process.env.JEST_WORKER_ID) { + initCommands(); +} \ No newline at end of file diff --git a/config.js b/config.js index 575e73b9..36521a1c 100644 --- a/config.js +++ b/config.js @@ -1,7 +1,6 @@ const crypto = require('crypto'); -const merge = require('lodash').merge; -const clone = require('lodash').cloneDeep; const path = require('path'); +const { merge, cloneDeep } = require('lodash'); function createToken() { return crypto.randomBytes(64).toString('hex'); @@ -13,6 +12,7 @@ const DEFAULT_CONFIG = { diffLines: 3, excludeFields: [], ignoreExtraFields: false, + ignoreExtraArrayItems: false, normalizeJson: true, serverEnabled: true, serverHost: 'localhost', @@ -23,19 +23,19 @@ const DEFAULT_CONFIG = { const CONFIG_KEY = 'cypress-plugin-snapshots'; -let config = clone(DEFAULT_CONFIG); +let config = cloneDeep(DEFAULT_CONFIG); function resolveModulePath(filename) { const fullPath = require.resolve(filename); const parentDir = path.dirname(__dirname); return path.join( 'node_modules/', - path.relative(parentDir, fullPath) + path.relative(parentDir, fullPath), ); } function initConfig(initialConfig = {}) { - config = merge(config, clone(initialConfig)); + config = merge(config, cloneDeep(initialConfig)); config.DIFF_CSS_PATH = resolveModulePath('diff2html/dist/diff2html.css'); config.DIFF_JS_PATH = resolveModulePath('diff2html/dist/diff2html.js'); config.SOCKET_JS_PATH = resolveModulePath('socket.io-client/dist/socket.io.js'); @@ -55,5 +55,5 @@ module.exports = { CONFIG_KEY, initConfig, getServerUrl, - getConfig -}; \ No newline at end of file + getConfig, +}; diff --git a/constants.js b/constants.js index 16426577..eda2c266 100644 --- a/constants.js +++ b/constants.js @@ -5,5 +5,5 @@ const URL_PREFIX = '#snapshot-compare-'; module.exports = { MATCH, SAVE, - URL_PREFIX -} \ No newline at end of file + URL_PREFIX, +}; diff --git a/package.json b/package.json index 652bacf6..d31153b2 100644 --- a/package.json +++ b/package.json @@ -7,13 +7,26 @@ "type": "git", "url": "https://github.com/meinaart/cypress-plugin-snapshots.git" }, + "files": [ + "*.js" + ], + "engines": { + "node": ">=8.2.1" + }, + "bugs": { + "url": "https://github.com/meinaart/cypress-plugin-snapshots/issues" + }, "keywords": [ "cypress", "cypress-io", "cypress-plugin" ], - "author": "Meinaart van Straalen ", + "author": "Meinaart van Straalen", "license": "MIT", + "scripts": { + "test": "jest", + "lint": "eslint *.js" + }, "dependencies": { "bufferutil": "^4.0.0", "diff2html": "^2.4.0", @@ -27,5 +40,48 @@ }, "peerDependencies": { "cypress": "^3.1.0" + }, + "devDependencies": { + "eslint": "^5.7.0", + "eslint-config-airbnb-base": "^13.1.0", + "eslint-config-prettier": "^2.9.0", + "eslint-plugin-cypress": "^2.0.1", + "eslint-plugin-import": "^2.14.0", + "eslint-plugin-jest": "^21.25.1", + "jest": "^23.6.0", + "prettier": "^1.13.6" + }, + "prettier": { + "printWidth": 100, + "semi": true, + "singleQuote": true, + "trailingComma": "es5" + }, + "eslintConfig": { + "extends": [ + "airbnb-base", + "prettier" + ], + "plugins": [ + "cypress", + "jest" + ], + "env": { + "cypress/globals": true, + "jest/globals": true + }, + "rules": { + "no-param-reassign": 0, + "no-use-before-define": 0, + "global-require": 0 + } + }, + "jest": { + "testPathIgnorePatterns": [ + "/examples/", + "/node_modules/" + ], + "clearMocks": true, + "testEnvironment": "node" } } diff --git a/plugin-utils.js b/plugin-utils.js index b569a0f4..9a5e84de 100644 --- a/plugin-utils.js +++ b/plugin-utils.js @@ -1,7 +1,7 @@ const path = require('path'); const fs = require('fs-extra'); +const { merge } = require('lodash'); const { getConfig } = require('./config'); -const merge = require('lodash').merge; const { formatNormalizedJson, subjectToSnapshot } = require('./snapshot'); function getSnapshot(filename, snapshotTitle) { @@ -19,19 +19,19 @@ function getSnapshot(filename, snapshotTitle) { return false; } -function saveSnapshot(data) { - return updateSnapshot(data.snapshotFile, data.snapshotTitle, data.subject); -} - function updateSnapshot(filename, snapshotTitle, subject) { const store = fs.existsSync(filename) ? fs.readJSONSync(filename) : {}; store[snapshotTitle] = subjectToSnapshot(subject, getConfig().normalizeJson); fs.writeFileSync(filename, formatNormalizedJson(store)); - return merge({}, subject, {saved: true}); + return merge({}, subject, { saved: true }); +} + +function saveSnapshot(data) { + return updateSnapshot(data.snapshotFile, data.snapshotTitle, data.subject); } module.exports = { getSnapshot, saveSnapshot, - updateSnapshot -}; \ No newline at end of file + updateSnapshot, +}; diff --git a/plugin.js b/plugin.js index 7e3f6b25..44de343b 100644 --- a/plugin.js +++ b/plugin.js @@ -1,13 +1,11 @@ -const { MATCH, SAVE } = require('./constants'); const unidiff = require('unidiff'); +const { merge } = require('lodash'); +const { MATCH, SAVE } = require('./constants'); const { updateSnapshot, getSnapshot } = require('./plugin-utils'); const { subjectToSnapshot, formatJson, getSnapshotFilename } = require('./snapshot'); const { initConfig, CONFIG_KEY, getConfig } = require('./config'); const { initServer } = require('./save-server'); -const merge = require('lodash').merge; - -let config = getConfig(); function formatDiff(subject) { if (typeof subject === 'object') { @@ -20,7 +18,7 @@ function createDiff(expected, actual, snapshotTitle) { return unidiff.diffAsText(formatDiff(expected), formatDiff(actual), { aname: snapshotTitle, bname: snapshotTitle, - context: config.diffLines + context: getConfig().diffLines, }); } @@ -30,15 +28,27 @@ function createDiff(expected, actual, snapshotTitle) { * @param {object} expected * @returns {object} */ -function keepKeysFromExpected(subject, expected) { - if (Array.isArray(expected)) { - return expected.map((item, index) => { - return keepKeysFromExpected(subject[index], item); - }); - } else if (typeof expected === 'object') { - return Object.keys(expected) +function keepKeysFromExpected(subject, expected, keepConfig) { + const cfg = keepConfig || getConfig(); + + if (Array.isArray(expected) && Array.isArray(subject)) { + const origin = cfg.ignoreExtraArrayItems ? expected : subject; + + const result = origin + .filter((value, index) => index < subject.length) + .map((value, index) => keepKeysFromExpected(subject[index], expected[index] || value, cfg)); + + // Add extra items not existing in expected from subject to result + if (!cfg.ignoreExtraArrayItems && subject.length > expected.length) { + return [...result, ...subject.slice(result.length, subject.length)]; + } + + return result; + } if (typeof expected === 'object' && typeof subject === 'object') { + const origin = cfg.ignoreExtraFields ? expected : subject; + return Object.keys(origin) .reduce((result, key) => { - result[key] = keepKeysFromExpected(subject[key], expected[key]); + result[key] = keepKeysFromExpected(subject[key], expected[key], cfg); return result; }, {}); } @@ -46,21 +56,22 @@ function keepKeysFromExpected(subject, expected) { return subject; } -function matchSnapshot(data = {}) { - const snapshotFile = getSnapshotFilename(data.testFile); - const snapshotTitle = data.snapshotTitle; +function matchSnapshot({ + testFile, snapshotTitle, subject, options, +} = {}) { + const config = getConfig(); + const snapshotFile = getSnapshotFilename(testFile); const expected = getSnapshot(snapshotFile, snapshotTitle); - let actual = subjectToSnapshot(data.subject, config.normalizeJson); - - if (data.options && data.options.ignoreExtraFields) { - actual = keepKeysFromExpected(actual, expected); - } + const actual = keepKeysFromExpected(subjectToSnapshot(subject, config.normalizeJson), expected, merge({ + ignoreExtraArrayItems: config.ignoreExtraArrayItems, + ignoreExtraFields: config.ignoreExtraFields + }, options)); const exists = expected !== false; const autoPassed = (config.autopassNewSnapshots && expected === false); const passed = (expected && formatDiff(expected) === formatDiff(actual)); - const diff = passed || autoPassed ? undefined : createDiff(expected, actual, data.snapshotTitle); + const diff = passed || autoPassed ? undefined : createDiff(expected, actual, snapshotTitle); let updated = false; @@ -77,22 +88,22 @@ function matchSnapshot(data = {}) { passed: passed || autoPassed, snapshotFile, snapshotTitle, - subject: data.subject, + subject, updated, }; return result; } -function saveSnapshot(data = {}) { - return updateSnapshot(data.snapshotFile, data.snapshotTitle, data.subject); +function saveSnapshot({ snapshotFile, snapshotTitle, subject }) { + return updateSnapshot(snapshotFile, snapshotTitle, subject); } function initPlugin(on, globalConfig = {}) { - config = initConfig(globalConfig.env[CONFIG_KEY] || {}); + const config = initConfig(globalConfig.env[CONFIG_KEY] || {}); if (globalConfig.env[CONFIG_KEY]) { merge(globalConfig.env, { - [CONFIG_KEY]: config + [CONFIG_KEY]: config, }); } @@ -100,10 +111,11 @@ function initPlugin(on, globalConfig = {}) { on('task', { [MATCH]: matchSnapshot, - [SAVE]: saveSnapshot + [SAVE]: saveSnapshot, }); } module.exports = { - initPlugin -}; \ No newline at end of file + initPlugin, + keepKeysFromExpected, +}; diff --git a/save-server.js b/save-server.js index cbc9fd05..556f263a 100644 --- a/save-server.js +++ b/save-server.js @@ -1,3 +1,6 @@ +/* eslint prefer-destructuring: 0 */ +const http = require('http'); +const socketio = require('socket.io'); const { SAVE } = require('./constants'); const { saveSnapshot } = require('./plugin-utils'); @@ -6,12 +9,12 @@ function initServer(config) { return; } - const server = require('http').createServer(); - const io = require('socket.io')(server); + const server = http.createServer(); + const io = socketio(server); - io.on('connection', function(client) { - let token = client.handshake.query.token; - client.on(SAVE, function(data) { + io.on('connection', (client) => { + const token = client.handshake.query.token; + client.on(SAVE, (data) => { if (token === config.token) { saveSnapshot(data); } @@ -23,4 +26,4 @@ function initServer(config) { module.exports = { initServer, -}; \ No newline at end of file +}; diff --git a/snapshot.js b/snapshot.js index f0c52aad..b7ed4632 100644 --- a/snapshot.js +++ b/snapshot.js @@ -6,12 +6,12 @@ const SNAPSHOT_TITLES = []; function getSnapshotFilename(testFile) { const dir = path.join(path.dirname(testFile), '__snapshots__'); - const filename = path.basename(testFile) + '.snap'; + const filename = `${path.basename(testFile)}.snap`; return path.join(dir, filename); } function getTestTitle(test) { - return (test.parent && test.parent.title ? getTestTitle(test.parent) + ' > ' : '') + test.title; + return (test.parent && test.parent.title ? `${getTestTitle(test.parent)} > ` : '') + test.title; } function snapshotTitleIsUsed(snapshotTitle) { @@ -22,12 +22,12 @@ function getSnapshotTitle(test) { const testTitle = getTestTitle(test); if (SNAPSHOTS[testTitle] !== undefined) { - SNAPSHOTS[testTitle]++; + SNAPSHOTS[testTitle] += 1; } else { SNAPSHOTS[testTitle] = 0; } - const snapshotTitle = testTitle + ' #' + SNAPSHOTS[testTitle]; + const snapshotTitle = `${testTitle} #${SNAPSHOTS[testTitle]}`; SNAPSHOT_TITLES.push(snapshotTitle); return snapshotTitle; } @@ -43,7 +43,9 @@ function formatNormalizedJson(subject) { function normalizeObject(subject) { if (Array.isArray(subject)) { return subject.map(normalizeObject); - } else if (typeof subject === 'object') { + } + + if (typeof subject === 'object') { const keys = Object.keys(subject); keys.sort(); @@ -61,7 +63,9 @@ function removeExcludedFields(subject) { if (excludedFields) { if (Array.isArray(subject)) { return subject.map(removeExcludedFields); - } else if (typeof subject === 'object') { + } + + if (typeof subject === 'object') { return Object.keys(subject) .filter(key => excludedFields.indexOf(key) === -1) .reduce((result, key) => { @@ -91,5 +95,5 @@ module.exports = { getSnapshotTitle, getTestTitle, subjectToSnapshot, - snapshotTitleIsUsed -} \ No newline at end of file + snapshotTitleIsUsed, +}; diff --git a/ui.js b/ui.js index e3595b8f..711fb273 100644 --- a/ui.js +++ b/ui.js @@ -1,10 +1,11 @@ +/* eslint-env browser */ const { SAVE, URL_PREFIX } = require('./constants'); const { getServerUrl, CONFIG_KEY } = require('./config'); -const NO_LOG = {log: false}; +const NO_LOG = { log: false }; function initUi() { - const $head = Cypress.$(parent.window.document.head); + const $head = Cypress.$(window.parent.window.document.head); const config = Cypress.env(CONFIG_KEY); if ($head.find('#cypress-plugin-snapshot').length > 0) { @@ -190,4 +191,4 @@ function initUi() { module.exports = { initUi, -}; \ No newline at end of file +};