mirror of
https://github.com/datashard/snapshot.git
synced 2024-11-21 13:42:28 +00:00
redo a surprising amount of things
This commit is contained in:
parent
7c6f997bc0
commit
cb39e029c3
15 changed files with 115 additions and 229 deletions
BIN
.github/assets/config.png
vendored
BIN
.github/assets/config.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 76 KiB |
22
LICENSE
Normal file
22
LICENSE
Normal file
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2023-2024 Joshua <data@shard.wtf>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
171
README.md
171
README.md
|
@ -1,53 +1,46 @@
|
|||
# @datashard/snapshot
|
||||
|
||||
> Adds value / object / DOM element snapshot testing support to Cypress test runner
|
||||
> Adds support for Value, Object, and Dom Element Snapshot Testing to Cypress
|
||||
|
||||
<details>
|
||||
<summary>Changes between <code>@datashard/snapshot</code> and <code>@cypress/snapshot</code></summary>
|
||||
<br>
|
||||
They're mostly the same, as this is a fork of the Latter, though it's not a drop-in replacement.
|
||||
|
||||
Unlike `@cypress/snapshot`, this saves snapshots in their own files with a sensible default and strives to have ongoing Support for future Cypress Versions
|
||||
|
||||
</details>
|
||||
|
||||
<!-- [![NPM][npm-icon] ][npm-url] -->
|
||||
## Breaking Changes
|
||||
> [!WARNING]
|
||||
The `readFileMaybe` task was required in previous Versions, this has been changed so this Module now uses the `cy.fixture` Command to get the contents of existing files. This means that this module will only be able to write new tests if `updateSnapshots` (previously `SNAPSHOT_UPDATE`) is set to true through Environment Variables or through the Cypress config.
|
||||
\
|
||||
This also means, that previous tests will likely be broken, *please make sure that your tests pass before updating to the latest version of this module*
|
||||
|
||||
## Install
|
||||
|
||||
Requires [Node](https://nodejs.org/en/) version 10 or above.
|
||||
Requires Node 16 or above
|
||||
|
||||
```sh
|
||||
npm install --save-dev @datashard/snapshot
|
||||
npm i --save-dev @datashard/snapshot
|
||||
```
|
||||
|
||||
## Import
|
||||
|
||||
After installing, you need to add this snippet within your Cypress Support File (default: `cypress/support/e2e.{js,ts}`)
|
||||
After Installing, you'll need to add the following import into your Commands/Support File
|
||||
> by default this will be `cypress/support/e2e.js`
|
||||
|
||||
```js
|
||||
require("@datashard/snapshot").register();
|
||||
require('@datashard/snapshot').regsiter()
|
||||
```
|
||||
|
||||
This registers a new command to create new snapshot or compare value to old snapshot
|
||||
This will register a new Command `.snapshot()`, to create new Snapshots and once created, to compare their Values.
|
||||
|
||||
and add the following to your `cypress.config.{js,ts}`
|
||||
## Config
|
||||
You can pass `updateSnapshots` and `useFolders` as options in the `cypress.config.js` file
|
||||
|
||||
```js
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
require("@datashard/snapshot").tasks(on, config)
|
||||
},
|
||||
```
|
||||
<!-- ![Example Settings for the Module](./.github/assets/config.png) -->
|
||||
|
||||
> **Note** \
|
||||
> \
|
||||
> `@datashard/snapshot` **requires** the `readFileMaybe` plugin to be included, which can be easily done using the code above
|
||||
Alternatively, you can also add `snapshotUpdate` as an Environment Variable to update your snapshots.
|
||||
|
||||
# Usage
|
||||
Simply pass `--env updateSnapshots=true` when running Cypress.
|
||||
|
||||
Currently, if you want to take more than one snapshot, you need to pass a Step Name to prevent overwrites / test failures
|
||||
> If you don't use the default fixture folder, you will also need to add `snapshotPath` to this module's config with the same path you use for `fixtureFolder`.
|
||||
|
||||
## Usage
|
||||
|
||||
If properly added, usage of this plugin is rather simple, simply add `.snapshot()` to cypress functions that return valid JSON.
|
||||
|
||||
### Example
|
||||
```js
|
||||
describe("my tests", () => {
|
||||
it("works", () => {
|
||||
|
@ -59,113 +52,27 @@ describe("my tests", () => {
|
|||
});
|
||||
```
|
||||
|
||||
In the above case, you can find the stored snapshot in their own files, mentioned above them
|
||||
Depending on your settings, this module will then save your snapshots as
|
||||
```ts
|
||||
// useFolders: false
|
||||
cypress/fixtures/snapshots/my-tests__works__foo.json
|
||||
cypress/fixtures/snapshots/my-tests__works__bar.json
|
||||
|
||||
// useFolders: true
|
||||
cypress/fixtures/snapshots/my-tests/works/foo.json
|
||||
cypress/fixtures/snapshots/my-tests/works/bar.json
|
||||
|
||||
```jsonc
|
||||
// cypress/fixtures/snapshots/my-tests-works-foo.json
|
||||
{ "foo": 42 }
|
||||
// cypress/fixtures/snapshots/my-tests-works-bar.json
|
||||
{ "bar": 101 }
|
||||
```
|
||||
|
||||
If you change the site values, the saved snapshot will no longer match, throwing an error
|
||||
Snapshots will generally be saved using this convention, provided by Cypress:
|
||||
|
||||
(picture taken from `cypress/snapshots/Arrays.json`)
|
||||
![Snapshot mismatch](.github/assets/updated-mismatch.png)
|
||||
|
||||
Click on the `SNAPSHOT` step in the Command Log to see expected and current value printed in the DevTools.
|
||||
|
||||
### Options
|
||||
|
||||
You can control snapshot comparison and behavior through a few options.
|
||||
|
||||
```js
|
||||
cy.get(...).snapshot({
|
||||
snapshotName: 'Snapshot Name', // Overwrite the generated Snapshot name
|
||||
snapshotPath: 'cypress/fixtures/not_snapshots', // Overwrite where the Snapshot should be stored
|
||||
json: false // convert DOM elements into JSON
|
||||
}) // when storing in the snapshot file
|
||||
|
||||
// will save as
|
||||
// cypress/not_snapshots/Snapshot-Name.json
|
||||
```
|
||||
{fixtureFolder}/<Context>-<Describe>-<It>-<Name?>.json
|
||||
{fixtureFolder}/<Context>/<Describe>/<It>/<Name?>.json
|
||||
```
|
||||
|
||||
You can also pass a "Step Name" to the Function
|
||||
If a step wasn't named, it will instead use the `<It>`for the file name, though this means that you will not be able to have more than 1 Snapshot in your It Block, as it would overwrite the previously created Snapshot files.
|
||||
|
||||
```js
|
||||
cy.get(...).snapshot("Intercepted API Request")
|
||||
// will save as
|
||||
// cypress/snapshots/<context>-<describe>-<it>-Intercepted-API-Request.json
|
||||
// to prevent duplications
|
||||
```
|
||||
|
||||
or both
|
||||
|
||||
```js
|
||||
cy.get(...).snapshot("Intercepted API Request", {
|
||||
snapshotPath: "cypress/snapshots/api",
|
||||
snapshotName: "first_intercept"
|
||||
})
|
||||
|
||||
// will save as
|
||||
// cypress/snapshots/api/first_intercept.json
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
This module provides some configuration options:
|
||||
|
||||
#### snapshot.snapshotPath
|
||||
|
||||
Sets the default Path for saving Snapshots (default: `cypress/snapshots`)
|
||||
|
||||
![Config Screenshot](./.github/assets/config.png)
|
||||
|
||||
#### `ENV` CYPRESS_UPDATE_SNAPSHOTS
|
||||
|
||||
Lets you pass a Env Variable to update failing Tests with the new Data
|
||||
|
||||
#
|
||||
|
||||
### Small print
|
||||
|
||||
Authors:
|
||||
|
||||
- Joshua <[data@shard.wtf](mailto:data@shard.wtf)>
|
||||
- Gleb Bahmutov <gleb@cypress.io>
|
||||
|
||||
<br>
|
||||
License: MIT - do anything with the code, but don't blame us if it does not work.
|
||||
|
||||
Support: If you find any problems with this module [open an issue](https://github.com/datashard/snapshot/issues) on Github
|
||||
|
||||
## MIT License
|
||||
|
||||
Copyright (c) 2017-2022 Cypress.io <hello@cypress.io> & Joshua <data@shard.wtf>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
[npm-icon]: https://nodei.co/npm/@datashard/snapshot.svg?downloads=true
|
||||
[npm-url]: https://npmjs.org/package/@datashard/snapshot
|
||||
[semantic-url]: https://github.com/semantic-release/semantic-release
|
||||
[renovate-badge]: https://img.shields.io/badge/renovate-app-blue.svg
|
||||
[renovate-app]: https://renovateapp.com/
|
||||
Of course, if a value changed, it will no longer match the snapshot and throw an Error.
|
||||
![](./.github/assets/Error.png)
|
|
@ -3,7 +3,7 @@ const { defineConfig } = require("cypress");
|
|||
module.exports = defineConfig({
|
||||
|
||||
snapshot: {
|
||||
updateSnapshots: true,
|
||||
// updateSnapshots: true,
|
||||
useFolders: true,
|
||||
},
|
||||
|
||||
|
@ -12,4 +12,4 @@ module.exports = defineConfig({
|
|||
// implement node event listeners here
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
|
@ -48,13 +48,13 @@ describe("datashard/snapshot", () => {
|
|||
cy.wrap({
|
||||
"status": 200,
|
||||
"response": {
|
||||
"array": [0, 1, 2, "Three"],
|
||||
"array": [0, 1, 2, "4"],
|
||||
"object": {
|
||||
"with": "more details"
|
||||
// "with": "more details"
|
||||
}
|
||||
}
|
||||
}
|
||||
).snapshot()
|
||||
},
|
||||
"thisisnew": "wtf"
|
||||
}).snapshot()
|
||||
})
|
||||
it("works based on fixtures", () => {
|
||||
cy
|
||||
|
|
|
@ -3,25 +3,21 @@
|
|||
describe("Random Describe", () => {
|
||||
context("Random Context", () => {
|
||||
it("Random It", () => {
|
||||
cy.fixture("File").snapshot("Fixture File", {
|
||||
snapshotName: "Random Fixture File"
|
||||
});
|
||||
// cy.fixture("File2").snapshot("Fixture File",
|
||||
cy.wrap(42).snapshot("Numbers");
|
||||
cy.wrap("foo-bar").snapshot("Strings");
|
||||
cy.wrap([1, 2, 3]).snapshot("Arrays");
|
||||
});
|
||||
|
||||
// it("works with numbers", () => {
|
||||
// console.log(cy.wrap(42))
|
||||
// cy.wrap(42).snapshot();
|
||||
// });
|
||||
|
||||
// it("works with strings", () => {
|
||||
// console.log(cy.wrap("foo-bar"))
|
||||
// cy.wrap("foo-bar").snapshot();
|
||||
// });
|
||||
|
||||
// it("works with arrays", () => {
|
||||
// console.log(cy.wrap([1, 2, 3]))
|
||||
// cy.wrap([1, 2, 3]).snapshot();
|
||||
// });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"data": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"data": 42
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"data": "foo-bar"
|
||||
}
|
|
@ -5,10 +5,9 @@
|
|||
0,
|
||||
1,
|
||||
2,
|
||||
"Three"
|
||||
"4"
|
||||
],
|
||||
"object": {
|
||||
"with": "more details"
|
||||
}
|
||||
}
|
||||
"object": {}
|
||||
},
|
||||
"thisisnew": "wtf"
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"jsonapi": {
|
||||
"version": "2.0"
|
||||
},
|
||||
"included": [
|
||||
{
|
||||
"type": "users",
|
||||
"id": "2",
|
||||
"attributes": {
|
||||
"name": "Test"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
{
|
||||
"status": 200,
|
||||
"response": {
|
||||
"array": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
"Three"
|
||||
],
|
||||
"object": {
|
||||
"with": "more details"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@
|
|||
"unused-deps": "dependency-check --unused --no-dev . --entry src/index.js",
|
||||
"semantic-release": "semantic-release",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:update": "cypress run --env SNAPSHOT_UPDATE=true",
|
||||
"cypress:update": "cypress run --env updateSnapshots=true",
|
||||
"cypress:run": "cypress run"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -11,10 +11,16 @@ const pickSerializer = (asJson, value) => {
|
|||
return identity;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
const parseTextToJSON = (text) => text.replace(/\| [✅➖➕⭕]/g, "").trim().replace(/(.*?),\s*(\}|])/g, "$1$2").replace(/},(?!")$/g, "}");
|
||||
|
||||
const store_snapshot = (props = { value, name, raiser }) => {
|
||||
if (Cypress.env().updateSnapshots || Cypress.config('snapshot').updateSnapshots) {
|
||||
cy.log(props.name)
|
||||
console.log(props.name)
|
||||
cy.writeFile(`${props.name}.json`, JSON.stringify(props.value, null, 2))
|
||||
} else {
|
||||
// TODO: Figure out how to replace the fixture folder name if people move it
|
||||
|
@ -39,6 +45,7 @@ const set_snapshot = ({ snapshotName, serialized, value }) => {
|
|||
|
||||
const raiser = ({ value, expected }) => {
|
||||
const result = compareValues({ expected, value });
|
||||
// console.log("Final Result", result.result)
|
||||
if ((!Cypress.env().updateSnapshots || !Cypress.config('snapshot').updateSnapshots) && !result.success) {
|
||||
devToolsLog = {
|
||||
...devToolsLog,
|
||||
|
@ -49,10 +56,13 @@ const set_snapshot = ({ snapshotName, serialized, value }) => {
|
|||
|
||||
throw new Error(
|
||||
`Snapshot Difference found.\nPlease Update the Snapshot\n
|
||||
|
||||
|
||||
${JSON.stringify(result.result.replaceAll(/[╺┿╳]/g, ""), null, 2)}`
|
||||
);
|
||||
${JSON.stringify(
|
||||
JSON.parse(parseTextToJSON(result.result).replaceAll(/[╺┿╳]/g, "")), null, 2)
|
||||
.replaceAll(" ", " ")
|
||||
}
|
||||
|
||||
`);
|
||||
}
|
||||
};
|
||||
Cypress.log(options);
|
||||
|
|
|
@ -1,52 +1,20 @@
|
|||
const isNestedData = (expected, value) => {
|
||||
return (
|
||||
expected && value && typeof expected == `object` && typeof value == `object`
|
||||
);
|
||||
};
|
||||
|
||||
const containsDiffChars = str => ["╺", "┿", "╳"].some(emoji => str.includes(emoji));
|
||||
const isNestedData = (expected, value) => expected && value && typeof expected == 'object' && typeof value == 'object';
|
||||
|
||||
|
||||
const checkDataState = (expected, value) => {
|
||||
let result;
|
||||
|
||||
if (expected === value) {
|
||||
result = `| ✅ "${value}",`;
|
||||
} else if (expected == undefined) {
|
||||
result = `| ➖ "➖╺ ${JSON.stringify(expected)?.replaceAll('"', "'")}|${value?.replaceAll('"', "'")}",`;
|
||||
} else if (value == undefined) {
|
||||
result = `| ➕ "➕┿ ${JSON.stringify(expected)?.replaceAll('"', "'")}|${value?.replaceAll('"', "'")}",`;
|
||||
} else {
|
||||
result = `| ⭕ "⭕╳ ${JSON.stringify(expected)?.replaceAll('"', "'")}|${value?.replaceAll('"', "'")}",`;
|
||||
result = `| ⭕ "⭕╳ ${JSON.stringify(expected)?.replaceAll('"', "'")} | ${value?.replaceAll('"', "'")}",`;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} text
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
function parseTextToJSON(text) {
|
||||
const lines =
|
||||
// JSON.stringify(
|
||||
text
|
||||
.replace(/\| [✅➖➕⭕]/g, "").trim()
|
||||
.replace(/(.*?),\s*(\}|])/g, "$1$2")
|
||||
// )
|
||||
return lines;
|
||||
// return JSON.stringify(lines, null, 2);
|
||||
}
|
||||
|
||||
function containsDiffChars(str) {
|
||||
const emojis = ["╺", "┿", "╳"];
|
||||
return emojis.some(emoji => str.includes(emoji));
|
||||
}
|
||||
|
||||
const compare = (expected, value) => {
|
||||
if(value === undefined){
|
||||
throw new Error("Please provide Data to compare against.")
|
||||
}
|
||||
let compareResult = "";
|
||||
|
||||
if (isNestedData(expected, value)) {
|
||||
|
@ -62,6 +30,9 @@ const compare = (expected, value) => {
|
|||
dataY = expected;
|
||||
}
|
||||
|
||||
console.log('datax', dataX)
|
||||
console.log('dataY', dataY)
|
||||
|
||||
dataX.forEach(function (item, index) {
|
||||
const resultset = compare(item, dataY[index]);
|
||||
compareResult += resultset.result;
|
||||
|
@ -87,18 +58,14 @@ const compare = (expected, value) => {
|
|||
} else {
|
||||
compareResult = checkDataState(expected, value);
|
||||
}
|
||||
let result = parseTextToJSON(compareResult).replace(/(},)$/g, `}`);
|
||||
console.log("compareResult",compareResult)
|
||||
console.log("result", result.replace(/(},)$/g, `}`))
|
||||
// let result = compareResult;
|
||||
|
||||
try {
|
||||
return {
|
||||
success: !containsDiffChars(result),
|
||||
result,
|
||||
success: !containsDiffChars(compareResult),
|
||||
result: compareResult,
|
||||
};
|
||||
} catch (error) {
|
||||
return { success: false, result };
|
||||
return { success: false, result: compareResult };
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue