# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['pytest_insta']

package_data = \
{'': ['*']}

install_requires = \
['pytest>=6.0.2', 'wrapt>=1.12.1,<2.0.0']

entry_points = \
{'pytest11': ['insta = pytest_insta.plugin']}

setup_kwargs = {
    'name': 'pytest-insta',
    'version': '0.1.11',
    'description': 'A practical snapshot testing plugin for pytest',
    'long_description': '# pytest-insta\n\n![Build Status](https://github.com/vberlier/pytest-insta/workflows/CI/badge.svg)\n[![PyPI](https://img.shields.io/pypi/v/pytest-insta.svg)](https://pypi.org/project/pytest-insta/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytest-insta.svg)](https://pypi.org/project/pytest-insta/)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)\n\n> A practical snapshot testing plugin for pytest.\n\n<img align="right" width="50%" src="https://raw.githubusercontent.com/vberlier/pytest-insta/main/demo.svg?sanitize=true">\n\n```python\nassert snapshot() == "awesome!"\n```\n\n## Introduction\n\nSnapshot testing makes it easy to monitor and approve changes by comparing the result of an operation against a previous reference value.\n\nThis project borrows from a lot of other implementations to provide a pythonic, batteries included snapshot testing solution. It also tries to feel as native to [`pytest`](https://docs.pytest.org/en/stable/) as possible with its integrated review tool.\n\n### Features\n\n- Expressive and familiar assertion syntax\n- Can format text, binary, hexdump, json and pickle snapshots out-of-the-box\n- Can be extended with custom snapshot formats\n- Interactive review tool for inspecting and approving changes\n\n### Credits\n\n- [`insta`](https://github.com/mitsuhiko/insta) (rust)\n\n  Armin\'s work was the initial motivation for this project and inspired the reviewing workflow.\n\n- [`jest`](https://jestjs.io/docs/en/snapshot-testing) (javascript)\n\n  Jest enabled the mass adoption of snapshot testing throughout the JavaScript ecosystem and now basically stands as the reference when it comes to what snapshot testing is supposed to look like.\n\n## Installation\n\nThe package can be installed with `pip`.\n\n```bash\n$ pip install pytest-insta\n```\n\n## Getting Started\n\nThe `snapshot` fixture is a function that returns the current value of a snapshot.\n\n```python\ndef test_hello_world(snapshot):\n    assert snapshot() == "hello"\n```\n\n```bash\n$ pytest\n...\nCREATE snapshots/<prefix>__hello_world__0.txt\n```\n\nRunning this test will create a new text file in the `snapshots` directory. The next time pytest runs, the test will load the snapshot and compare it to the actual value.\n\nThe return value of the `snapshot` function can be assigned to a variable and used multiple times.\n\n```python\ndef test_hello_world(snapshot):\n    expected = snapshot()\n    assert expected == "hello"\n    assert expected.upper() == "HELLO"\n```\n\nBy default, each invocation of the `snapshot` function will generate its own snapshot.\n\n```python\ndef test_hello_world(snapshot):\n    assert snapshot() == "hello"\n    assert snapshot() == "world"\n```\n\n```bash\n$ pytest\n...\nCREATE snapshots/<prefix>__hello_world__0.txt\nCREATE snapshots/<prefix>__hello_world__1.txt\n```\n\nYou can also name snapshots explicitly. This makes it possible to load a snapshot multiple times during the same test.\n\n```python\ndef test_hello_world(snapshot):\n    assert snapshot("message.txt") == "hello"\n    assert snapshot("message.txt") == "hello"\n```\n\n```bash\n$ pytest\n...\nCREATE snapshots/<prefix>__hello_world__message.txt\n```\n\n## Snapshot Formats\n\nBy default, the `snapshot` fixture will store snapshots as `.txt` files. By providing a filename or just a specific file extension, you can create snapshots using various formats supported out-of-the-box.\n\n```python\ndef test_hello_world(snapshot):\n    assert snapshot("json") == {"hello": "world"}\n    assert snapshot("expected.json") == {"hello": "world"}\n```\n\n```bash\n$ pytest\n...\nCREATE snapshots/<prefix>__hello_world__0.json\nCREATE snapshots/<prefix>__hello_world__expected.json\n```\n\nNote that the plugin doesn\'t diff the snapshot files themselves but actually loads snapshots back into the interpreter and performs comparisons on live python objects. This makes it possible to use snapshot formats that aren\'t directly human-readable like pure binary files and pickle.\n\n| Built-in Formats | Extension  | Supported Types                              |\n| ---------------- | ---------- | -------------------------------------------- |\n| Plain text       | `.txt`     | `str`                                        |\n| Binary           | `.bin`     | `bytes`                                      |\n| Hexdump          | `.hexdump` | `bytes`                                      |\n| Json             | `.json`    | Any object serializable by the json module   |\n| Pickle           | `.pickle`  | Any object serializable by the pickle module |\n\nThe built-in formats should get you covered most of the time but you can also really easily implement your own snapshot formats.\n\n```python\nfrom dataclasses import dataclass\nfrom pathlib import Path\n\nfrom pytest_insta import Fmt\n\n@dataclass\nclass Point:\n    x: int\n    y: int\n\nclass FmtPoint(Fmt[Point]):\n    extension = ".pt"\n\n    def load(self, path: Path) -> Point:\n        return Point(*map(int, path.read_text().split()))\n\n    def dump(self, path: Path, value: Point):\n        path.write_text(f"{value.x} {value.y}")\n\ndef test_hello_world(snapshot):\n    assert snapshot("pt") == Point(4, 2)\n```\n\nYou can create a custom formatter by inheriting from the `Fmt` class and defining custom `load` and `dump` methods. The `extension` attribute associates the custom formatter to the specified file extension.\n\nCustom formatters can be defined anywhere in your test suite but it\'s recommended to keep them in `conftest.py` if they\'re meant to be used across multiple files.\n\n## Command-line Options\n\nThe plugin extends the `pytest` cli with a new `--insta` option that accommodates the snapshot-testing workflow. The option can be set to one of the following strategies:\n\n- `update` - Record and update differing snapshots\n- `update-new` - Record and create snapshots that don\'t already exist\n- `update-none` - Don\'t record or update anything\n- `record` - Record and save differing snapshots to be reviewed later\n- `review` - Record and save differing snapshots then bring up the review tool\n- `review-only` - Don\'t run tests and only bring up the review tool\n- `clear` - Don\'t run tests and clear all the snapshots to review\n\nIf the option is not specified, the strategy will default to `update-none` if `pytest` is running in a CI environment and `update-new` otherwise. This makes sure that your pipeline properly catches any snapshot you might forget to push while keeping the development experience seamless by automatically creating snapshots as you\'re writing tests.\n\nThe `record` option is useful if you\'re in the middle of something and your snapshots keep changing. Differing snapshots won\'t cause tests to fail and will instead be recorded and saved.\n\n```bash\n$ pytest --insta record\n...\nRECORD .pytest_cache/d/insta/<prefix>__hello_world__0.txt\n\nNOTICE 1 snapshot to review\n```\n\nWhen you\'re done making changes you can use the `review` option to bring up the review tool after running your tests. Each differing snapshot will display a diff and let you inspect the new value and the old value in a python repl.\n\n```bash\n$ pytest --insta review\n...\n_____________________________ [1/1] _____________________________\n\nold: snapshots/example_hello_world__0.txt\nnew: .pytest_cache/d/insta/example_hello_world__0.txt\n\n>       assert old == new\nE       assert \'hello\' == \'world\'\nE         - world\nE         + hello\n\ntest_example.py:1: test_hello_world\n\na: accept, r: reject, s: skip\n>>>\n```\n\nFinally, the `update` option will let you update any differing snapshot according to the current test run, without going through the review tool.\n\n```bash\n$ pytest --insta update\n...\nUPDATE snapshots/<prefix>__hello_world__0.txt\n```\n\nIt\'s worth mentioning that the updating, recording and reviewing strategies take into account any filter you might specify with the `-k` or `-m` options.\n\n## Caveats\n\nThe `snapshot` fixture hijacks equality checks to record changes. This keeps assertions expressive and readable but introduces two caveats that you need to be aware of.\n\n- **Right-sided snapshots ❌**\n\n  If an object\'s `__eq__` method doesn\'t return `NotImplemented` when its type doesn\'t match the compared object, the snapshot won\'t be able to record the updated value if it\'s placed on the right side of the comparison.\n\n  <details>\n  <summary>\n  Explanation\n  </summary>\n\n  ***\n\n  Strings return `NotImplemented` when compared to non-string objects so the following test will behave as expected.\n\n  ```python\n  def test_bad(snapshot):\n      assert "hello" == snapshot()  # This works\n  ```\n\n  However, dataclasses return `False` when compared to objects of different types and won\'t let the snapshot record any changes when placed on the left-side of the comparison.\n\n  ```python\n  from dataclasses import dataclass\n  from pathlib import Path\n\n  from pytest_insta import Fmt\n\n  @dataclass\n  class Point:\n      x: int\n      y: int\n\n  class FmtPoint(Fmt[Point]):\n      extension = ".pt"\n\n      def load(self, path: Path) -> Point:\n          return Point(*map(int, path.read_text().split()))\n\n      def dump(self, path: Path, value: Point):\n          path.write_text(f"{value.x} {value.y}")\n\n  def test_bad(snapshot):\n      assert Point(4, 2) == snapshot("pt")  # This doesn\'t work\n  ```\n\n  </details>\n\n  **Recommendation ✅**\n\n  To avoid confusion and keep things consistent, always put snapshots on the left-side of the comparison.\n\n  ```python\n  def test_good(snapshot):\n      assert snapshot() == "hello"\n  ```\n\n  ```python\n  def test_good(snapshot):\n      assert snapshot("pt") == Point(4, 2)\n  ```\n\n- **Not comparing snapshots ❌**\n\n  Snapshots should first be compared to their actual value before being used in other expressions and assertions.\n\n  <details>\n  <summary>\n  Explanation\n  </summary>\n\n  ***\n\n  The comparison records the current value if the snapshot doesn\'t exist yet. In the following example, the test will fail before the actual comparison and the snapshot will not be generated.\n\n  ```python\n  def test_bad(snapshot):\n      expected = snapshot()\n      assert expected.upper() == "HELLO"  # This doesn\'t work\n      assert expected == "hello"\n  ```\n\n  ```bash\n  $ pytest\n  ...\n  >       assert expected.upper() == "HELLO"\n  E       AttributeError: \'SnapshotNotfound\' object has no attribute \'upper\'\n  ```\n\n  </details>\n\n  **Recommendation ✅**\n\n  Always compare the snapshot to its actual value first and only perform additional operations afterwards.\n\n  ```python\n  def test_good(snapshot):\n      expected = snapshot()\n      assert expected == "hello"\n      assert expected.upper() == "HELLO"\n  ```\n\n## Contributing\n\nContributions are welcome. Make sure to first open an issue discussing the problem or the new feature before creating a pull request. The project uses [`poetry`](https://python-poetry.org).\n\n```bash\n$ poetry install\n```\n\nYou can run the tests with `poetry run pytest`.\n\n```bash\n$ poetry run pytest\n```\n\nThe project must type-check with [`pyright`](https://github.com/microsoft/pyright). If you\'re using VSCode the [`pylance`](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) extension should report diagnostics automatically. You can also install the type-checker locally with `npm install` and run it from the command-line.\n\n```bash\n$ npm run watch\n$ npm run check\n```\n\nThe code follows the [`black`](https://github.com/psf/black) code style. Import statements are sorted with [`isort`](https://pycqa.github.io/isort/).\n\n```bash\n$ poetry run isort pytest_insta tests\n$ poetry run black pytest_insta tests\n$ poetry run black --check pytest_insta tests\n```\n\n---\n\nLicense - [MIT](https://github.com/vberlier/pytest-insta/blob/master/LICENSE)\n',
    'author': 'Valentin Berlier',
    'author_email': 'berlier.v@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/vberlier/pytest-insta',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
