diff --git a/README.md b/README.md
index dbb673b..3df1c1f 100644
--- a/README.md
+++ b/README.md
@@ -5,100 +5,70 @@ Vue 3 Snapshot Serialization for Vitest and Jest.
This is the successor to [jest-serializer-vue-tjw](https://github.com/tjw-lint/jest-serializer-vue-tjw) (Vue 2, Jest, CJS).
-## Plan
-
-1. New repo tech stack:
- * ESM `import`
- * Vite + Vitest + Vitest snapshots
- * Vue 3
- * GHA - Linting/Unit tests
-1. Settings will now be stored differently:
- * Settings no longer stored in files (`package.json`, `vue.config.js`, `vite.config.js`, etc.)
- * Instead `globalThis.vueSnapshots = {};` will be used for settings.
- * This allows users to define settings in their `global.beforeEach()` in their settings file.
- * Also makes it much easier to override these global settings when you have test-specific settings.
- * Would be be nice to abstract the settings gathering from the serialization, so the serialization can be externalized.
- * `serializeVue(htmlOrVueWrapper, settings);`
- * Would allow E2E tooling to import and use this directly
- * The library would need to clear this global setting after every run to prevent global object-mutation based test-bleed.
-1. Migration guide
-1. Once feature support reaches an acceptable point, update the old repo to point people to this one.
- * Place deprecation warning
- * Point to migration guide, maybe migration guide should just live in the old repo and be linked to from the new one?
-
-
-## Planned API Support:
-
-This is mostly taken from `jest-serializer-vue-tjw`:
-
-Setting | In new version? | Description
-:-- | :-- | :--
-formatting | Yes, may change | Formmating options object, including new "diffable html" options
-removeDataVId | Yes | Removes `data-v-1234abcd=""` from your snapshots
-removeComments | Yes | Removes all HTML comments
-removeDataTest | Yes | Removes `data-test="whatever"` from your snapshots
-removeDataTestid | Yes | Removes `data-testid="whatever"` from your snapshots
-removeDataTestId | Yes | Removes `data-test-id="whatever"` from your snapshots
-removeDataQa | Yes | Removes `data-qa="whatever"` from your snapshots
-removeDataCy | Yes | Removes `data-cy="whatever"` from your snapshots (Cypress)
-removeDataPw | Yes - **New** | Removes `data-pw="whatever"` from your snapshots (Playwright)
-removeServerRendered | Yes | Removes `data-server-rendered="true"` from your snapshots
-sortAttributes | Eventually | Sorts the attributes inside HTML elements in the snapshot. May not be in first release of v4
-attributesToClear | Probably | Array of attribute strings to remove the values from. `['title', 'id']` produces ``
-verbose | Probably | Logs to the console errors or other messages if true
-removeClassTest | Maybe | Removes all CSS classes that start with "test", `class="test-whatever"`
-removeIdTest | Maybe | Removes `id="test-whatever"` or `id="testWhatever"`from snapshots
-clearInlineFunctions | Maybe | `
` becomes `
`
-removeIstanbulComments | No | I cannot reproduce this issue anymore. Will add it back in if people run into it again.
-addInputValues | No | Display form field value. `` becomes ``. Not sure how to do this in Vue 3
-stringifyObjects | No | Replaces `title="[object Object]"` with `title="{a:'asdf'}"`. Not sure if this is possible in Vue 3
-
-
-## New planned features
-
-Not in `jest-serializer-vue`
-
-* Remove Playwright tokens (`data-pw="whatever`)
-* Diffable HTML (See [#85](https://github.com/tjw-lint/jest-serializer-vue-tjw/issues/85))
-* Support for E2E tooling like Playwright (see [#70](https://github.com/tjw-lint/jest-serializer-vue-tjw/issues/70))
-
-
-* * *
-
-
-## Implemented
+## Usage
+
+1. `npm install --save-dev vue3-snapshot-serializer`
+1. **Vitest:**
+ * In your `vite.config.js` or `vitest.config.js`:
+ ```js
+ import { defineConfig } from 'vite'; // or 'vitest'
+
+ export default defineConfig({
+ test: {
+ snapshotSerializers: [
+ './node_modules/vue3-snapshot-serializer/index.js'
+ ]
+ }
+ });
+ ```
+1. **Jest:**
+ * In your `package.json`, or Jest config file:
+ ```json
+ {
+ "jest": {
+ "snapshotSerializers": [
+ "./node_modules/vue3-snapshot-serializer/index.js"
+ ]
+ }
+ }
+ ```
+
+
+## Features
The following features are implemented in this library:
-Setting | Default | Description
-:-- | :-- | :--
-verbose | `true` | Logs to the console errors or other messages if true. **Strongly recommended** if using experimental features.
-attributesToClear | [] | Takes an array of attribute strings, like `['title', 'id']`, to remove the values from these attributes. ``.
-sortAttributes | `true` | Sorts the attributes inside HTML elements in the snapshot. This helps make snapshot diffs easier to read.
-removeServerRendered | `true` | Removes `data-server-rendered="true"` from your snapshots if true.
-removeDataVId | `true` | Removes `data-v-1234abcd=""` from your snapshots.
-removeDataTest | `true` | Removes `data-test="whatever"` from your snapshots if true. To also remove these from your production builds, [see here](https://github.com/cogor/vite-plugin-vue-remove-attributes).
-removeDataTestid | `true` | Removes `data-testid="whatever"` from your snapshots if true.
-removeDataTestId | `true` | Removes `data-test-id="whatever"` from your snapshots if true.
-removeDataQa | `false` | Removes `data-qa="whatever"` from your snapshots if true. `data-qa` is usually used by non-dev QA members. If they change in your snapshot, that indicates it may break someone else's E2E tests. So most using `data-qa` prefer they be left in by default.
-removeDataCy | `false` | Removes `data-cy="whatever"` from your snapshots if true. `data-cy` is used by Cypress end-to-end tests. If they change in your snapshot, that indicates it may break an E2E tests. So most using `data-cy` prefer they be left in by default.
-removeDataPw | `false` | Removes `data-pw="whatever"` from your snapshots if true. `data-pw` is used by Playwright end-to-end tests. If they change in your snapshot, that indicates it may break an E2E tests. So most using `data-pw` prefer they be left in by default.
-removeIdTest | `false` | Removes `id="test-whatever"` or `id="testWhatever"`from snapshots. **Warning:** You should never use ID's for test tokens, as they can also be used by JS and CSS, making them more brittle. Use `data-test-id` instead.
-removeClassTest | `false` | Removes all CSS classes that start with "test", `class="test-whatever"`. **Warning:** Don't use this approach. Use `data-test` instead. It is better suited for this because it doesn't conflate CSS and test tokens.
-removeComments | `false` | Removes all HTML comments from your snapshots. This is false by default, as sometimes these comments can infer important information about how your DOM was rendered. However, this is mostly just personal preference.
-clearInlineFunctions | `false` | Replaces `
` or this `
` with this placeholder `
`.
-formatting | `'diffable'` | Function to use for formatting the markup output. See examples below. Accepts `'none'`, `'diffable'`, or a custom function handed a string of markup and must return a string.
-
-
-## Formatting examples:
+Setting | Default | Description
+:-- | :-- | :--
+`verbose` | `true` | Logs to the console errors or other messages if true.
+`attributesToClear` | [] | Takes an array of attribute strings, like `['title', 'id']`, to remove the values from these attributes. ``.
+`addInputValues` | `true` | Display internal element value on `input`, `textarea`, and `select` fields. `` becomes ``.
+`sortAttributes` | `true` | Sorts the attributes inside HTML elements in the snapshot. This helps make snapshot diffs easier to read.
+`removeServerRendered` | `true` | Removes `data-server-rendered="true"` from your snapshots if true.
+`removeDataVId` | `true` | Removes `data-v-1234abcd=""` from your snapshots if true.
+`removeDataTest` | `true` | Removes `data-test="whatever"` from your snapshots if true. To also remove these from your production builds, [see here](https://github.com/cogor/vite-plugin-vue-remove-attributes).
+`removeDataTestid` | `true` | Removes `data-testid="whatever"` from your snapshots if true.
+`removeDataTestId` | `true` | Removes `data-test-id="whatever"` from your snapshots if true.
+`removeDataQa` | `false` | Removes `data-qa="whatever"` from your snapshots if true. `data-qa` is usually used by non-dev QA members. If they change in your snapshot, that indicates it may break someone else's E2E tests. So most using `data-qa` prefer they be left in by default.
+`removeDataCy` | `false` | Removes `data-cy="whatever"` from your snapshots if true. `data-cy` is used by Cypress end-to-end tests. If they change in your snapshot, that indicates it may break an E2E test. So most using `data-cy` prefer they be left in by default.
+`removeDataPw` | `false` | Removes `data-pw="whatever"` from your snapshots if true. `data-pw` is used by Playwright end-to-end tests. If they change in your snapshot, that indicates it may break an E2E test. So most using `data-pw` prefer they be left in by default.
+`removeIdTest` | `false` | Removes `id="test-whatever"` or `id="testWhatever"`from snapshots. **Warning:** You should never use ID's for test tokens, as they can also be used by JS and CSS, making them more brittle and their intent less clear. Use `data-test-id` instead.
+`removeClassTest` | `false` | Removes all CSS classes that start with "test", like `class="test-whatever"`. **Warning:** Don't use this approach. Use `data-test` instead. It is better suited for this because it doesn't conflate CSS and test tokens.
+`removeComments` | `false` | Removes all HTML comments from your snapshots. This is false by default, as sometimes these comments can infer important information about how your DOM was rendered. However, this is mostly just personal preference.
+`clearInlineFunctions` | `false` | Replaces `
` or this `
` with this placeholder `
`.
+`formatting` | `'diffable'` | Function to use for formatting the markup output. See examples below. Accepts `'none'`, `'diffable'`, or a function.
+
+
+### Formatting examples:
There are 3 formatting options:
* None - does not apply any additional formatting
+* Diffable - Applies formatting designed for more easily readble diffs
* Custom function - You can pass in your own function to format the markup.
-* Diffable - Applies formatting designed for more easily readble diffs (example below)
-**Input:**
+
+#### **Input Example:**
```html
@@ -107,7 +77,8 @@ There are 3 formatting options:
```
+
+
+## Adjusting settings
+
+In your `setup.js` file, I would recommend creating
+
+```js
+global.beforeEach(() => {
+ global.vueSnapshots = {
+ // Your custom settings, such as:
+ verbose: true
+ };
+});
+```
+
+With this in place, your snapshot settings will be reset before each test runs. This means you can freely override these settings in specific tests, like so:
+
+```js
+import { mount } from '@vue/test-utils';
+
+import MyComponent from '@/components/MyComponent.vue';
+
+describe('MyComponent', () => {
+ test('My test', () => {
+ // Test-specific settings
+ global.vueSnapshots.attributesToClear = ['data-uuid'];
+
+ expect(MyComponent)
+ .toMatchSnapshot();
+ });
+});
+```
+
+
+## Using this library outside of Vitest/Jest
+
+This library has many great features for formatting and cleaning up markup. For example, you may want to create your own function to validate expected markup in an End-to-End (E2E) testing tool, like Playwright or Cypress.
+
+```js
+import { vueMarkupFormatter } from 'vue3-snapshot-serializer';
+
+globalThis.vueSnapshots = {
+ // Your settings
+};
+
+const formatted = vueMarkupFormatter('
Text
');
+console.log(formatted);
+//`
+// Text
+//
`
+```
+
+The `vueMarkupFormatter` function expects a string starting with `<`, and will return a formatted string based on your `globalThis.vueSnapshots` settings. You can use `global`, `globalThis`, or `window` to set the `vueSnapshots` settings object depending on your JavaScript environment.
diff --git a/index.js b/index.js
index 5c7b0f3..b86ad55 100644
--- a/index.js
+++ b/index.js
@@ -1,6 +1,5 @@
import { isHtmlString, isVueWrapper } from './src/helpers.js';
import { loadOptions } from './src/loadOptions.js';
-import { vNodeManipulation } from './src/vNodeManipulation.js';
import { stringManipulation } from './src/stringManipulation.js';
import { formatMarkup } from './src/formatMarkup.js';
@@ -25,7 +24,6 @@ const test = function (received) {
const print = function (received) {
loadOptions();
let html = received || '';
- html = vNodeManipulation(html);
html = stringManipulation(html);
return formatMarkup(html);
diff --git a/src/cheerioManipulation.js b/src/cheerioManipulation.js
index 3ef2566..9d3f63d 100644
--- a/src/cheerioManipulation.js
+++ b/src/cheerioManipulation.js
@@ -1,6 +1,10 @@
import * as cheerio from 'cheerio';
import * as htmlparser2 from 'htmlparser2';
+import {
+ stringify,
+ swapQuotes
+} from '@/helpers.js';
import { removeTestTokens } from '@/removeTestTokens.js';
/**
@@ -24,6 +28,34 @@ const cheerioize = function (html) {
return $;
};
+/**
+ * Appends a value attribute to input, select, and textareas
+ * to show the current value of the element in the snapshot.
+ *
+ *
+ *
+ *
+ * @param {object} $ The markup as a cheerio object
+ * @param {object} vueWrapper The Vue-Test Utils mounted component wrapper
+ */
+const addInputValues = function ($, vueWrapper) {
+ if (
+ globalThis.vueSnapshots?.addInputValues &&
+ typeof(vueWrapper?.findAll) === 'function'
+ ) {
+ const inputSelectors = 'input, textarea, select';
+ const inputs = vueWrapper.findAll(inputSelectors);
+
+ if (inputs?.at && inputs.at(0)) {
+ $(inputSelectors).each(function (index, element) {
+ const input = inputs.at(index);
+ const value = input.element.value;
+ element.attribs.value = swapQuotes(stringify(value));
+ });
+ }
+ }
+};
+
/**
* This removes data-v-1234abcd="" from your snapshots.
*
@@ -152,9 +184,34 @@ const sortAttributes = function ($) {
}
};
-export const cheerioManipulation = function (html) {
+/**
+ * Applies desired DOM manipulations based on
+ * global.vueSnapshots settings for improved snapshots.
+ *
+ * @param {Object|string} vueWrapper Either the Vue-Test-Utils mounted component object, or a string of html.
+ * @return {string} String of manipulated HTML, ready for formatting.
+ */
+export const cheerioManipulation = function (vueWrapper) {
+ let html = vueWrapper;
+ if (typeof(vueWrapper?.html) === 'function') {
+ html = vueWrapper.html();
+ }
+
+ /**
+ * NOTE: Although we could check the settings and potentially skip
+ * the cheerioze step completely, that would result in inconsistent
+ * snapshots, as Cheerio removes empty attribute assignments.
+ *
+ * `` becomes ``
+ *
+ * Because of this, we should always pass the markup through Cheerio
+ * to keep all snapshots consistent, even if we are not doing any
+ * DOM manipulation.
+ */
+
const $ = cheerioize(html);
+ addInputValues($, vueWrapper);
removeServerRenderedText($);
removeTestTokens($);
removeScopedStylesDataVIDAttributes($);
diff --git a/src/helpers.js b/src/helpers.js
index 0fcc4f3..2ed68dc 100644
--- a/src/helpers.js
+++ b/src/helpers.js
@@ -24,3 +24,73 @@ export const logger = function (message) {
console.info('Vue 3 Snapshot Serializer: ' + message);
}
};
+
+/**
+ * Swaps single and double quotes.
+ *
+ * 'Text' => "Text"
+ * "Text" => 'Text'
+ *
+ * @param {string} string Input
+ * @return {string} Swapped output
+ */
+export const swapQuotes = function (string) {
+ return string.replace(/['"]/g, function (match) {
+ return match === '"' ? '\'' : '"';
+ });
+};
+
+/**
+ * Same as JSON.stringify, but without quotes around object properties.
+ *
+ * @param {object} obj data to stringify
+ * @return {string} stringified string
+ */
+export const stringify = function (obj) {
+ if (obj === null) {
+ return 'null';
+ }
+ if (obj === undefined) {
+ return 'undefined';
+ }
+ if (Number.isNaN(obj)) {
+ return 'NaN';
+ }
+ if (obj === Infinity) {
+ return 'Infinity';
+ }
+ if (obj === -Infinity) {
+ return '-Infinity';
+ }
+ if (typeof(obj) === 'number') {
+ return String(obj);
+ }
+ if (obj instanceof Error) {
+ return 'Error: ' + obj.message;
+ }
+ if (obj instanceof Set) {
+ return JSON.stringify([...obj]);
+ }
+ if (typeof(obj) === 'object' && typeof(obj.getTime) === 'function') {
+ if (Number.isNaN(obj.getTime())) {
+ return obj.toString(); // 'Invalid Date'
+ } else {
+ return obj.getTime() + ''; // '1583463154386'
+ }
+ }
+ if (typeof(obj) === 'function') {
+ return 'Function';
+ }
+ if (typeof(obj) !== 'object' || Array.isArray(obj)) {
+ return JSON.stringify(obj) || '';
+ }
+
+ let props = Object
+ .keys(obj)
+ .map((key) => {
+ return key + ':' + stringify(obj[key]);
+ })
+ .join(',');
+
+ return '{' + props + '}';
+};
diff --git a/src/loadOptions.js b/src/loadOptions.js
index 022546a..c8e85d7 100644
--- a/src/loadOptions.js
+++ b/src/loadOptions.js
@@ -2,6 +2,7 @@ import { logger } from '@/helpers.js';
export const booleanDefaults = {
verbose: true,
+ addInputValues: true,
sortAttributes: true,
removeServerRendered: true,
removeDataVId: true,
diff --git a/src/stringManipulation.js b/src/stringManipulation.js
index 99982ff..d75dd30 100644
--- a/src/stringManipulation.js
+++ b/src/stringManipulation.js
@@ -6,8 +6,8 @@ import { cheerioManipulation } from '@/cheerioManipulation.js';
* Multi-line
* Containing HTML
*
- * @param {string} html The markup being serialized
- * @return {string} Modified HTML string
+ * @param {string} html The markup being serialized
+ * @return {string} Modified HTML string
*/
function removeAllComments (html) {
if (globalThis.vueSnapshots?.removeComments) {
diff --git a/src/vNodeManipulation.js b/src/vNodeManipulation.js
deleted file mode 100644
index 489d453..0000000
--- a/src/vNodeManipulation.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export const vNodeManipulation = function (vueWrapper) {
- return vueWrapper;
-};
diff --git a/tests/mockComponents/SeveralInputs.vue b/tests/mockComponents/SeveralInputs.vue
new file mode 100644
index 0000000..e2c225f
--- /dev/null
+++ b/tests/mockComponents/SeveralInputs.vue
@@ -0,0 +1,31 @@
+
+