Code Monkey home page Code Monkey logo

phpunit-snapshot-assertions's Introduction

Snapshot testing with PHPUnit

Latest Version on Packagist Software License run-tests Total Downloads

Snapshot testing is a way to test without writing actual test cases.

You can learn more in this free video from our Testing Laravel course. Don't worry you can use this package in non-Laravel projects too.

use Spatie\Snapshots\MatchesSnapshots;

class OrderTest
{
    use MatchesSnapshots;

    public function test_it_casts_to_json()
    {
        $order = new Order(1);

        $this->assertMatchesJsonSnapshot($order->toJson());
    }
}

On the first run, the test runner will create a new snapshot.

> ./vendor/bin/phpunit

There was 1 incomplete test:

1) OrderTest::test_it_casts_to_json
Snapshot created for OrderTest__test_it_casts_to_json__1

OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 0, Incomplete: 1.

On subsequent runs, the test will pass as long as the snapshot doesn't change.

> ./vendor/bin/phpunit

OK (1 test, 1 assertion)

If there's a regression, the test will fail!

$orderId = new Order(2); // Regression! Was `1`
> ./vendor/bin/phpunit

1) OrderTest::test_it_casts_to_json
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
Failed asserting that '{"id":2}' matches JSON string "{
    "id": 1
}

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require --dev spatie/phpunit-snapshot-assertions

Usage

To make snapshot assertions, use the Spatie\Snapshots\MatchesSnapshots trait in your test case class. This adds a set of assertion methods to the class:

  • assertMatchesSnapshot($actual)
  • assertMatchesFileHashSnapshot($actual)
  • assertMatchesFileSnapshot($actual)
  • assertMatchesHtmlSnapshot($actual)
  • assertMatchesJsonSnapshot($actual)
  • assertMatchesObjectSnapshot($actual)
  • assertMatchesTextSnapshot($actual)
  • assertMatchesXmlSnapshot($actual)
  • assertMatchesYamlSnapshot($actual)
  • assertMatchesImageSnapshot($actual)

Snapshot Testing 101

Let's do a snapshot assertion for a simple string, "foo".

public function test_it_is_foo() {
    $this->assertMatchesSnapshot('foo');
}

The first time the assertion runs, it doesn't have a snapshot to compare the string with. The test runner generates a new snapshot and marks the test as incomplete.

> ./vendor/bin/phpunit

There was 1 incomplete test:

1) ExampleTest::test_it_matches_a_string
Snapshot created for ExampleTest__test_it_matches_a_string__1

OK, but incomplete, skipped, or risky tests!
Tests: 1, Assertions: 0, Incomplete: 1.

Snapshot ids are generated based on the test and testcase's names. Basic snapshots return a plain text or YAML representation of the actual value.

foo

Let's rerun the test. The test runner will see that there's already a snapshot for the assertion and do a comparison.

> ./vendor/bin/phpunit

OK (1 test, 1 assertion)

If we change actual value to "bar", the test will fail because the snapshot still returns "foo".

public function test_it_is_foo() {
    $this->assertMatchesSnapshot('bar');
}
> ./vendor/bin/phpunit

1) ExampleTest::test_it_matches_a_string
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'foo'
+'bar'

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

When we expect a changed value, we need to tell the test runner to update the existing snapshots instead of failing the test. This is possible by adding a-d --update-snapshots flag to the phpunit command, or setting the UPDATE_SNAPSHOTS env var to true.

> ./vendor/bin/phpunit -d --update-snapshots

OK (1 test, 1 assertion)

As a result, our snapshot file returns "bar" instead of "foo".

bar

File snapshots

The MatchesSnapshots trait offers two ways to assert that a file is identical to the snapshot that was made the first time the test was run:

The assertMatchesFileHashSnapshot($filePath) assertion asserts that the hash of the file passed into the function and the hash saved in the snapshot match. This assertion is fast and uses very little disk space. The downside of this assertion is that there is no easy way to see how the two files differ if the test fails.

The assertMatchesFileSnapshot($filePath) assertion works almost the same way as the file hash assertion, except that it actually saves the whole file in the snapshots directory. If the assertion fails, it places the failed file next to the snapshot file so they can easily be manually compared. The persisted failed file is automatically deleted when the test passes. This assertion is most useful when working with binary files that should be manually compared like images or pdfs.

Image snapshots

The assertImageSnapshot requires the spatie/pixelmatch-php package to be installed.

This assertion will pass if the given image is nearly identical to the snapshot that was made the first time the test was run. You can customize the threshold by passing a second argument to the assertion. Higher values will make the comparison more sensitive. The threshold should be between 0 and 1.

$this->assertMatchesImageSnapshot($imagePath, 0.1);

Customizing Snapshot Ids and Directories

Snapshot ids are generated via the getSnapshotId method on the MatchesSnapshot trait. Override the method to customize the id. By default, a snapshot id exists of the test name, the test case name and an incrementing value, e.g. Test__my_test_case__1.

Example: Replacing the __ Delimiter With --

protected function getSnapshotId(): string
{
    return (new ReflectionClass($this))->getShortName().'--'.
        $this->name().'--'.
        $this->snapshotIncrementor;
}

By default, snapshots are stored in a __snapshots__ directory relative to the test class. This can be changed by overriding the getSnapshotDirectory method.

Example: Renaming the __snapshots__ directory to snapshots

protected function getSnapshotDirectory(): string
{
    return dirname((new ReflectionClass($this))->getFileName()).
        DIRECTORY_SEPARATOR.
        'snapshots';
}

Using specific Drivers

The driver used to serialize the data can be specificied as second argument of the assertMatchesSnapshot method, so you can pick one that better suits your needs:

use Spatie\Snapshots\Drivers\JsonDriver;
use Spatie\Snapshots\MatchesSnapshots;

class OrderTest
{
    use MatchesSnapshots;

    public function test_snapshot_with_json_driver()
    {
        $order = new Order(1);

        $this->assertMatchesSnapshot($order->toJson(), new JsonDriver());
    }
}

Writing Custom Drivers

Drivers ensure that different types of data can be serialized and matched in their own way. A driver is a class that implements the Spatie\Snapshots\Driver interface, which requires three method implementations: serialize, extension and match.

Let's take a quick quick look at the JsonDriver.

namespace Spatie\Snapshots\Drivers;

use PHPUnit\Framework\Assert;
use Spatie\Snapshots\Driver;
use Spatie\Snapshots\Exceptions\CantBeSerialized;

class JsonDriver implements Driver
{
    public function serialize($data): string
    {
        if (! is_string($data)) {
            throw new CantBeSerialized('Only strings can be serialized to json');
        }

        return json_encode(json_decode($data), JSON_PRETTY_PRINT).PHP_EOL;
    }

    public function extension(): string
    {
        return 'json';
    }

    public function match($expected, $actual)
    {
        Assert::assertJsonStringEqualsJsonString($actual, $expected);
    }
}
  • The serialize method returns a string which will be written to the snapshot file. In the JsonDriver, we'll decode and re-encode the json string to ensure the snapshot has pretty printing.
  • We want to save json snapshots as json files, so we'll use json as their file extension.
  • When matching the expected data with the actual data, we want to use PHPUnit's built in json assertions, so we'll call the specific assertJsonStringEqualsJsonString method.

Drivers can be used by passing them as assertMatchesSnapshot's second argument.

$this->assertMatchesSnapshot($something->toYaml(), new MyYamlDriver());

Usage in CI

When running your tests in Continuous Integration you would possibly want to disable the creation of snapshots.

By using the --without-creating-snapshots parameter or by setting the CREATE_SNAPSHOTS env var to false, PHPUnit will fail if the snapshots don't exist.

> ./vendor/bin/phpunit -d --without-creating-snapshots

1) ExampleTest::test_it_matches_a_string
Snapshot "ExampleTest__test_it_matches_a_string__1.txt" does not exist.
You can automatically create it by removing the `CREATE_SNAPSHOTS=false` env var, or `-d --no-create-snapshots` of PHPUnit's CLI arguments.

Usage with parallel testing

If you want to run your test in parallel with a tool like Paratest, ou with the php artisan test --parallel command of Laravel, you will have to use the environment variables.

> CREATE_SNAPSHOTS=false php artisan test --parallel

1) ExampleTest::test_it_matches_a_string
Snapshot "ExampleTest__test_it_matches_a_string__1.txt" does not exist.
You can automatically create it by removing the `CREATE_SNAPSHOTS=false` env var, or `-d --no-create-snapshots` of PHPUnit's CLI arguments.

A note for Windows users

Windows users should configure their line endings in .gitattributes.

# Snapshots used in tests hold serialized data and their line ending should be left unchanged
tests/**/__snapshots__/** binary

Changelog

Please see CHANGELOG for more information what has changed recently.

Testing

composer test

Contributing

Please see CONTRIBUTING for details.

Security

If you've found a bug regarding security please mail [email protected] instead of using the issue tracker.

Postcardware

You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.

Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.

We publish all received postcards on our company website.

Credits

License

The MIT License (MIT). Please see License File for more information.

phpunit-snapshot-assertions's People

Contributors

adrianmrn avatar akoepcke avatar alexvanderbist avatar bramdevries avatar brendt avatar chris-doehring avatar dependabot[bot] avatar freekmurze avatar github-actions[bot] avatar gndk avatar gummibeer avatar ianrodrigues avatar jakubkulhan avatar jaybizzle avatar kanti avatar khartir avatar kojirock5260 avatar mallardduck avatar mathieutu avatar mfn avatar nclavaud avatar nielsvanpach avatar patinthehat avatar romainnorberg avatar sebastiandedeyne avatar simonhammes avatar sjorso avatar stevemoretz avatar su-kun1899 avatar vhenzl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phpunit-snapshot-assertions's Issues

Inconsistent line endings

Ran into a problem while on-boarding a developer who's working on Windows: All of the snapshot tests are failing due to \r\n instead of the expected \n.

Strangely enough, this is also the case when running the tests from within the Linux VM.

Versions:

  • spatie/phpunit-snapshot-assertions 2.0.0
  • Windows 10
  • Vagrant 2.2.7
  • VirtualBox 6.1.4

I can't tell from the changelog and past issues if this is something that's already been fixed, and we're not currently in the position to upgrade in order to test it.

Updating to PHPUnit 11.x outputs deprecation notices

Metadata found in doc-comment for method Tests\OutputTest::setUpSnapshotIncrementor(). Metadata in doc-comments is deprecated and will no longer be supported in PHPUnit 12. Update your test code to use attributes instead.

Metadata found in doc-comment for method Tests\OutputTest::markTestIncompleteIfSnapshotsHaveChanged(). Metadata in doc-comments is deprecated and will no longer be supported in PHPUnit 12. Update your test code to use attributes instead.

Incorrect HTML output

We have many emails generated by MJML that use conditional comments. There is a particular conditional comment to Prevent IE Edge Meta Tag from Breaking Images in Live Mail that looks like this...

<!--[if !mso]><!-->
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!--<![endif]-->

When this is run though assertMatchesHtmlSnapshot() it gets changed, incorrectly, to...

<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge">\n
<!--<![endif]-->

Adding LIBXML_HTML_NODEFDTD as the second parameter to loadHtml() here seems to fix the "issue"

I'm not sure why, hence why I have created an issue rather than a PR.

Here's a failing and passing example ready to run in Tinkerwell.

Failing example

$data = <<<EOT
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">

<head>
    <title></title>
    <!--[if !mso]><!-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--<![endif]-->
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
    <p>Hello</p>
</body>

</html>
EOT;


$domDocument = new DOMDocument('1.0');
$domDocument->preserveWhiteSpace = false;
$domDocument->formatOutput = true;

@$domDocument->loadHTML($data); // to ignore HTML5 errors

$domDocument->saveHTML();

// OUTPUT:

// <!DOCTYPE html>\n
// <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">\n
// <head>\n
// <title></title>\n
// <!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge">\n
// <!--<![endif]--><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n
// </head>\n
// <body>\n
//     <p>Hello</p>\n
// </body>\n
// </html>\n

Passing example

$data = <<<EOT
<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">

<head>
    <title></title>
    <!--[if !mso]><!-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--<![endif]-->
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
    <p>Hello</p>
</body>

</html>
EOT;


$domDocument = new DOMDocument('1.0');
$domDocument->preserveWhiteSpace = false;
$domDocument->formatOutput = true;

@$domDocument->loadHTML($data, LIBXML_HTML_NODEFDTD); // to ignore HTML5 errors

$domDocument->saveHTML();

// OUTPUT:

// <!DOCTYPE html>\n
// <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">\n
// \n
// <head>\n
//     <title></title>\n
//     <!--[if !mso]><!-->\n
//     <meta http-equiv="X-UA-Compatible" content="IE=edge">\n
//     <!--<![endif]-->\n
//     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n
// </head>\n
// <body>\n
//     <p>Hello</p>\n
// </body>\n
// \n
// </html>\n

Add option to ignore different line breaks

Different line breaks are an issue when developers of a project are using different operating systems.
It would be good, to have an argumment to ignore/normalize different line breaks (\n vs \r\n) for matching.

Return snapshot object from assertion

Hi,
I need to make some operations with the file generated by the assertion, and for that, it would be really useful to get the snapshot object.

If you're ok with that, I can make a PR which return the snapshot from the assertion.

Thanks.
Mathieu.

UTF8 support for assertMatchesHtmlSnapshot

Following code replaces utf8 characters with html entities in snapshot:

<?php

use PHPUnit\Framework\TestCase;
use Spatie\Snapshots\MatchesSnapshots;

final class SomeTest extends TestCase
{
    use MatchesSnapshots;

    public function testUtf(): void
    {
        $this->assertMatchesHtmlSnapshot('<p>żółć देवनागरी Кириллица</p>');
    }
}

Result:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><p>&Aring;&frac14;&Atilde;&sup3;&Aring;&#130;&Auml;&#135; &agrave;&curren;&brvbar;&agrave;&yen;&#135;&agrave;&curren;&micro;&agrave;&curren;&uml;&agrave;&curren;&frac34;&agrave;&curren;&#151;&agrave;&curren;&deg;&agrave;&yen;&#128; &ETH;&#154;&ETH;&cedil;&Ntilde;&#128;&ETH;&cedil;&ETH;&raquo;&ETH;&raquo;&ETH;&cedil;&Ntilde;&#134;&ETH;&deg;</p></body></html>

Maybe assertMatchesHtmlSnapshot could accept encoding as a second parameter (it could be passed to new HtmlDriver($encoding) and then to $domDocument = new DOMDocument('1.0', $encoding);?

[Proposal] a matches file snapshot assertion

I'm working on two different projects where i either generate or manipulate images. I'm unit testing this using the the assertMatchesFileHashSnapshot assertion that i added to this package a while ago. This works well, but its not ideal: there is no way to see what the image is supposed to look like, and if the test fails there is no easy way to compare the two images manually.

If i want to see an image now my workflow is to put an exit statement in my unit test to prevent it from emptying the temporary directory i save the image to, and then manually checking the image.

A few days ago i made a custom assertion method that makes testing images (but also pdfs or any kind of binary file) way easier. I was wondering if it would be accepted if i made a PR.

The new assertMatchesFileSnapshot($filePath) would work like this:

If no file snapshot exists yet, it puts the file in the __snapshots__/files/ directory with getSnapshotId() . $fileExtension as a file name (so for example: /tests/__snapshots__/files/ImageTest__it_can_transform_an_image__0.jpg)

When running the test again, it compares the hash of the file passed to assertMatchesFileSnapshot to the hash of the snapshot file. If the hashes don't match, the test fails.

If the test fails, it puts the failed test file in the files directory with a suffix, for example: /tests/__snapshots__/files/ImageTest__it_can_transform_an_image__0_failed.jpg. Now you can easily compare the two files, and see why the test failed. The assertMatchesFileSnapshot will automatically clean up failed files before asserting, so if your tests are green the files directory only contains valid snapshot files.

I've been using this method for a couple of days, and its a huge improvement over what i used to do. I was wondering what you guys think about this idea, and if it would be accepted if i made a PR?

Requirement of symfony/yaml ^4.2 is too restrictive

Thanks for the update to make this library compatible with Phpunit 8!

I have a bit of an issue with the requirement symfony/yaml ^4.2, which forces me to update my entire project to Symfony 4.2 (we're on 4.1), just to do a PHPUnit upgrade. symfony/yaml is not doing much, so I'd bet any 4.x or event 3.x version would also work fine for you. So there's actually no need to lock the users of your library into using 4.2+. I'd suggest making the requirement a bit more open and require a reasonable minimum version that is working fine for your use-case, giving people more freedom in which dependencies they use in which version.

add HTML driver

Hey,

I thought it's possible to use the XmlDriver for HTML but this only works as long as the HTML isn't a whole page which starts with <!DOCTYPE html>. In this case it will throw an exception.

ErrorException: DOMDocument::loadXML(): Opening and ending tag mismatch: link line 12 and head in Entity, line: 14

So I would say that adding a new HtmlDriver would be useful?

namespace Spatie\Snapshots\Drivers;

use DOMDocument;
use PHPUnit\Framework\Assert;
use Spatie\Snapshots\Driver;
use Spatie\Snapshots\Exceptions\CantBeSerialized;

class HtmlDriver implements Driver
{
    public function serialize($data): string
    {
        if (! is_string($data)) {
            throw new CantBeSerialized('Only strings can be serialized to html');
        }

        $domDocument = new DOMDocument('1.0');
        $domDocument->preserveWhiteSpace = false;
        $domDocument->formatOutput = true;

        @$domDocument->loadHTML($data);

        return $domDocument->saveHTML();
    }

    public function extension(): string
    {
        return 'html';
    }

    public function match($expected, $actual)
    {
        Assert::assertEquals($expected, $this->serialize($actual));
    }
}

Updating snapshots renders error on PHPUnit v8.0

Update: This is most likely a issue with phpunit where the ini_set signature typehints string but phpunit sets boolean.

I have created an issue at phpunit

sebastianbergmann/phpunit#3535

Hello,

Thank you for an awesome library, I use it in many projects and I love it! Great job!

I have however encountered an error and I'm not sure if it is me or an actual bug but I thought I would share it here and see if you have an idea.

I started out with this configuration (from composer.json):

"require-dev": {
    "phpunit/phpunit": "^7.1",
    "spatie/phpunit-snapshot-assertions": "^1.3"
}

Updating snapshots works just fine:

$ vendor/bin/phpunit tests/src/MyClassTest.php -d --update-snapshots

1) MyClassTest::testResult
Snapshot updated for MyClassTest__testResult__1
...

However, after upgrading to newest version of phpunit and the snapshot assertion library (composer.json):

"require-dev": {
    "phpunit/phpunit": "^8.0",
    "spatie/phpunit-snapshot-assertions": "^2.1"
}

I got this error:

$ vendor/bin/phpunit tests/src/MyClassTest.php -d --update-snapshots
PHP Fatal error:  Uncaught TypeError: ini_set() expects parameter 2 to be string, boolean given in /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php:379
Stack trace:
#0 /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php(379): ini_set('--update-snapsh...', true)
#1 /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php(175): PHPUnit\TextUI\Command->handleArguments(Array)
#2 /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php(164): PHPUnit\TextUI\Command->run(Array, true)
#3 /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/phpunit(61): PHPUnit\TextUI\Command::main()
#4 {main}
  thrown in /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php on line 379

Fatal error: Uncaught TypeError: ini_set() expects parameter 2 to be string, boolean given in /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php on line 379

TypeError: ini_set() expects parameter 2 to be string, boolean given in /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php on line 379

Call Stack:
    0.0005     395656   1. {main}() /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/phpunit:0
    0.0038     853360   2. PHPUnit\TextUI\Command::main() /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/phpunit:61
    0.0038     853472   3. PHPUnit\TextUI\Command->run() /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php:164
    0.0038     853472   4. PHPUnit\TextUI\Command->handleArguments() /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php:175
    0.0041     881392   5. ini_set() /Users/oli/PhpstormProjects/refactoring/vendor/phpunit/phpunit/src/TextUI/Command.php:379


Variables in local scope (#4):
  $argv = array (0 => 'vendor/bin/phpunit', 1 => 'tests/src/MyClassTest.php', 2 => '-d', 3 => '--update-snapshots')
  $bootstrapScript = *uninitialized*
  $configuration = *uninitialized*
  $configurationFile = *uninitialized*
  $file = *uninitialized*
  $generator = *uninitialized*
  $handler = *uninitialized*
  $includePath = *uninitialized*
  $ini = array (0 => '--update-snapshots')
  $option = array (0 => 'd', 1 => '--update-snapshots')
  $optionName = *uninitialized*
  $phpunitConfiguration = *uninitialized*
  $src = *uninitialized*
  $t = *uninitialized*
  $test = *uninitialized*
  $testSuite = *uninitialized*
  $testsDirectory = *uninitialized*

Tried on two PHP versions (7.2.9) and (7.3.2), both throw the same error.

I also set the second parameter to string in phpunit (/TextUI/Command.php:379)

\ini_set($ini[0], "");

That solved the problem, although I'm not sure where in the problem lies since looking at the source in phpunit reveals this portion of the code hasn't been changed for years.

https://github.com/sebastianbergmann/phpunit/blame/master/src/TextUI/Command.php#L373

Don't automatically create new snapshots when in CI

Hi,
I had a problem with case insensitive os in local, and case sensitive in CI.
The result was that all the snapshots was recreated in the CI, and we didn't notice, because the CI was green.

It would be really nice to block the snapshot creation in CI environment.

We can automatically use a package to detect if we are running in CI (found quickly this one), or let the user block them via a config.

This is related to #47 (comment) but not the same, as here, we don't want to skip, but to fail!

Thanks.
Mathieu.

Cannot see diff of failing tests

Hello, I am not able to see the diff of two failing tests.
Errors occurs on:

PHPUnit 8.1.3 by Sebastian Bergmann and contributors. Runtime: PHP 7.2.17

Sample outcome of failing test:

There was 1 failure:                                                                                                                                                                                                
                                                                                                                                                                                                                    
1) TemplateTest::awesomeTest                                                                                                                                                   
Failed asserting that two strings are equal.                                                                                                                                                                        
                                                                                                                                                                                                                    
Snapshots can be updated by passing -d --update-snapshots through PHPUnit's CLI arguments.                                                                                                               
Failed asserting that true is false.                                                                                                                                                                                
                                                                                                                                                                                                                    
/app/tests/TemplateTest.php:100  

Expected behaviour:

There was 1 failure:                                                                                                                                                                                                
                                                                                                                                                                                                                    
1) TemplateTest::awesomeTest                                                                                                                                                   
Failed asserting that two strings are equal.                                                                                                                                                                        
--- Expected
+++ Actual
@@ @@
     <title>\n
     </title>\n
     <style type="text/css" media="all">\n
-        @import url('hello');\n
+        @import url('hello-world);\n
     </style>\n
 </head>\n
 <body>\n                                                                                                                                                                                                                    
Snapshots can be updated by passing -d --update-snapshots through PHPUnit's CLI arguments.                                                                                                               
Failed asserting that true is false.                                                                                                                                                                                
                                                                                                                                                                                                                    
/app/tests/TemplateTest.php:100  

A possible solution is to change MatchesSnapshots.php:261
from $newMessage = $exception->getMessage(). to $newMessage = $exception.

Assertions broken for tests with dataprovider

Generating (and asserting) snapshots is broken for tests that use dataproviders in 5.0.0:

PHP 8.2.1
phpunit/phpunit:10.0.7
spatie/phpunit-snapshot-assertions:5.0.0

Testcase

<?php

declare(strict_types=1);

use PHPUnit\Framework\TestCase;
use Spatie\Snapshots\MatchesSnapshots;

class SnapshotTest extends TestCase
{
    use MatchesSnapshots;

    public static function dataProvider(): Generator
    {
        yield [1];
        yield [2];
    }

    /** @dataProvider dataProvider */
    public function testSomething(int $value): void
    {
        $this->assertMatchesSnapshot($value);
    }
}

Result for 4.2.16 (expected)

PHPUnit 9.5.28 by Sebastian Bergmann and contributors.

II                                                                  2 / 2 (100%)

Time: 00:00.005, Memory: 6.00 MB

OK, but incomplete, skipped, or risky tests!
Tests: 2, Assertions: 2, Incomplete: 2.

Result for 5.0.0 (actual)

PHPUnit 10.0.7 by Sebastian Bergmann and contributors.

Runtime:       PHP 8.2.1

.F                                                                  2 / 2 (100%)

Time: 00:00.045, Memory: 8.00 MB

There was 1 failure:

1) SnapshotTest::testSomething with data set #1
Failed asserting that two strings are equal.

Snapshots can be updated by passing `-d --update-snapshots` through PHPUnit's CLI arguments.
--- Expected
+++ Actual
@@ @@
-'1'
+'2'

[redacted]

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

Optional symfony/yaml dependency

symfony/yaml:^4.0 seems a too much strong requirement for me since the YamlDriver could be not used at all and the Yaml::parse with content is accepted since symfony/yaml:^2.7.

I would suggest this way:

  • add to the composer.json a conflict with symfony/yaml:<2.7
  • move symfony/yaml from require to require-dev
  • implement the Spatie\Snapshots\Drivers\YamlDriver::__construct and throw an exception if the class Symfony\Component\Yaml\Yaml doesn't exist

The same result as now should be obtained removing not really necessary contraints.
What do you think @freekmurze ? I would be happy to create a pull request if this looks good to you.

Feature Request: Support for Tabular-Assertions as a snapshot format

Feature Description

I'd like to propose adding a driver to allow the use of tabular-assertions as the snapshot format. This would bring some benefits, mirroring those offered by tabular-assertions:

  • Enhanced readability
  • Improved error reporting
  • And to a lesser degree, the ability to hand edit the snapshots

Potential Challenges & Solutions

  • Dynamic values in snapshots: Integrating dynamic values might be challenging, especially when considering the --update-snapshots command. A workaround could be to initially support tabular assertions without dynamic values and explore this complexity in a later phase.

Additional Feature Suggestion

  • Optional fields parameter: An optional parameter to specify and limit the columns in a tabular assertion would be a useful addition. This would provide a convenient way to control the data scope in assertions, though users could also format the data manually before asserting.

I am interested to hear your thoughts, or any plans regarding this suggestion.

Thank you for considering this feature request.

FileSnapShot no longer includes the name of the data provider

This is a breaking change from v4, and I'll add an example later when I get a chance.

But in v4, with PhpUnit v9, the dataProvider name was used in the file snapshot.

In v5 with PhpUnit v10, it is not.

    public static function dataTestRun(): array
    {
        return [
            'no database' => [
                null,
            ],
            'mysql database' => [
                new Mysql(),
            ],
            'pgsql database' => [
                new Pgsql(),
            ],
        ];
    }

This used to result in 3 files being created for Php80Test::testRun of

Php80Test__testRun with data set mysql database__1.txt
Php80Test__testRun with data set no database__1.txt
Php80Test__testRun with data set pgsql database__1.txt

Now I only get 1 file, Php80Test__testRun__1.txt, which is missing the detail in the filename needed from the data provider to distinguish between 3 runs of the method for different values in the data provider.

I've had a quick look through the code and can't see anything obvious. I suspect the changes to PhpUnit 10 events is the culprit, but I'll add some test cases when I can and attempt to create a PR. I just wanted to make sure this was logged.

Installing package by composer breaks Symfony4 unit testing

When requiring spatie/phpunit-snapshot-assertions --dev package by composer the unit tests of Symfony breaks.

Output:

Fatal error: Declaration of Symfony\Bridge\PhpUnit\Legacy\SymfonyTestsListenerForV7::addError(PHPUnit\Framework\Test $test, Throwable $t, float $time): void must be compatible with PHPUnit\Framework\TestListener::addError(PHPUnit\Framework\Test $test, Exception $e, $time) in /var/www/html/vendor/symfony/phpunit-bridge/Legacy/SymfonyTestsListenerForV7.php on line 27

I've tried a couple of things

  • Remove vendor dir and install everything again
  • Remove vendor dir and run composer update
  • Remove vendor dir and load older version (1.2.3)

I guess there is a conflict in the PHP-Unit Bridge and another package.

Our package list:

    "require": {
        "php": ">=7.1",
        "aws/aws-sdk-php": "^3.24",
        "doctrine/doctrine-bundle": "^1.6",
        "doctrine/doctrine-cache-bundle": "^1.2",
        "doctrine/orm": "^2.5",
        "firebase/php-jwt": "^5.0",
        "guzzlehttp/guzzle": "^6.3",
        "harmbandstra/swagger-ui-bundle": "^3.0",
        "knplabs/doctrine-behaviors": "~1.1",
        "league/fractal": "master@dev",
        "myclabs/php-enum": "^1.6",
        "nelmio/cors-bundle": "^1.5",
        "sensio/framework-extra-bundle": "^5.1",
        "stof/doctrine-extensions-bundle": "^1.3",
        "symfony/asset": "^4.0",
        "symfony/flex": "^1.0",
        "symfony/form": "^4.0",
        "symfony/monolog-bundle": "^3.1",
        "symfony/orm-pack": "^1.0",
        "symfony/polyfill-apcu": "^1.0",
        "symfony/security-bundle": "^4.0",
        "symfony/serializer-pack": "^1.0",
        "symfony/swiftmailer-bundle": "^3.1",
        "symfony/translation": "^4.0",
        "symfony/twig-bundle": "^4.0",
        "symfony/validator": "^4.0",
        "symfony/yaml": "^4.0",
        "white-october/pagerfanta-bundle": "^1.1"
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0",
        "spatie/phpunit-snapshot-assertions": "^1.2",
        "symfony/browser-kit": "^4.0",
        "symfony/dotenv": "^4.0",
        "symfony/phpunit-bridge": "^4.0",
        "symfony/profiler-pack": "^1.0"
    },

Ps. Good useful package! Thanks a lot for maintaining this. (In our old office it works perfect)

Hack-free update flag

We're currently using PHPUnit's -d flag, would be nicer if it was it's own CLI option. phpunit/phpunit#2271 would fix this.

Getting "Class not found" errors when this library is installed

I am seeing very strange behavior when trying to use this library.

$ phpunit test/ReadTest.php 
Class 'test/ReadTest' could not be found in '/myapp/test/ReadTest.php'.

myapp contains a phpunit.xml file that looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="../testing/bootstrap.php">
    <testsuites>
        <testsuite name="PHP storage test">
            <directory>test</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory suffix=".php">./src</directory>
        </whitelist>
    </filter>
</phpunit>

The test cases extend PHPUnit\Framework\TestCase and run fine once I remove the dependency on "spatie/phpunit-snapshot-assertions": "^3.0"

Reconsider default driver for associative arrays

I'm mostly using this package to snapshot associative arrays, or something that can be easily converted to an associative array. So I'm looking to optimize this package for that use case.

When no driver is specified, we currently fall back to VarDriver. This is the easy way out, as it works for all datatypes, but the output is noisy.

<?php return array (
  0 => 
  array (
    'id' => 2405,
    'artist_id' => 3374,
  ),
  1 => 
  array (
    'id' => 2425,
    'artist_id' => 3395,
  ),
);

I think we should use the JsonDriver when dealing with associative arrays, which would give a nicer output.

[
    {
        "id": 2405,
        "artist_id": 3374
    },
    {
        "id": 2425,
        "artist_id": 3395
    }
]

Alternatively we could add a YamlDriver. People often complain about YAML, but for reasons that don't really matter when the file is going to be written and read by the same parser over and over again.

YAML sounds like a good idea over JSON because it's even more readable, and it produces better diffs (no comma changes, less changed lines due to bracket changes)

- id: 2405
  artist_id: 3374
- id: 2425
  artist_id: 3395

Tests are not correctly marked as incomplete when creating/updating snapshots

As the title says, tests are not correctly marked as Incomplete when creating/updating snapshots. Instead PHPUnit displays them as Errors.

PHP 8.2.4
laravel/framework 10.7.1
phpunit/phpunit 10.1.0
spatie/phpunit-snapshot-assertions 5.0.1

Manually calling $this->markTestIncomplete('Test incomplete') within a test works perfectly fine. But this packages calls this method from within the markTestIncompleteIfSnapshotsHaveChanged() method which is using an @after annotation. Exceptions thrown during this test lifecycle appear to be treated differently.

Testcases

<?php

use App\Models\User;
use Illuminate\Foundation\Testing\TestCase;
use Spatie\Snapshots\MatchesSnapshots;

class SnapshotTest extends TestCase
{
    use MatchesSnapshots;

    public function test_snapshot_matches()
    {
        $user = User::query()->findOrFail(1);

        $this->assertMatchesJsonSnapshot($user->toJson());
    }

    public function test_is_incomplete()
    {
        $this->markTestIncomplete('This test is incomplete.');
    }
}

Command

vendor/bin/phpunit -d --update-snapshots

Expected Result

PHPUnit 10.1.0 by Sebastian Bergmann and contributors.

II                                                                  2 / 2 (100%)

Time: 00:00.143, Memory: 34.00 MB

OK, but there are issues!
Tests: 2, Assertions: 3, Incomplete: 2.

Actual Result

PHPUnit 10.1.0 by Sebastian Bergmann and contributors.

EI                                                                  2 / 2 (100%)

Time: 00:00.154, Memory: 38.50 MB

ERRORS!
Tests: 2, Assertions: 3, Errors: 1, Incomplete: 1.

[Feature] get{TYPE}Snapshot($testName)

As a developer I would like to be able to load up a snap shot file to do reverse testing (conversions).

example:

public function it_can_decode_xml()
{
    $this->assertMatchesSnapshot($this->xml->decode($this->getXmlSnapshot('it_can_encode_xml'));
}

MongoDB ObjectId spanshots are wrong

The snapshots should contain the string representation (5deb63d67c562a39d75aa126) but they are timestamp in the current version of phpunit-snapshot-assertions. At the moment it is not a serious issue for me, but I am surprised to know that sometimes snapshots saved ObjectIds as null in older versions.

Test Case 1

public function test_object_id()
{
    $id = new MongoDB\BSON\ObjectId('5deb63d67c562a39d75aa126');
    $this->assertMatchesSnapshot($id);
}

Invalid yml Snapshot in ^4.2.6

timestamp: 1575707606

Correct php Snapshot in ^3.0.0

<?php return MongoDB\BSON\ObjectId::__set_state(array(
   'oid' => '5deb63d67c562a39d75aa126',
));

Test Case 2

public function test_object_id()
{
    $id = new MongoDB\BSON\ObjectId('5deb63d67c562a39d75aa126');
    $this->assertMatchesSnapshot([$id]); // <-- Array snapshot
}

Invalid yml Snapshot in ^4.2.6

-
    timestamp: 1575707606

Invalid yml Snapshot in ^3.0.0

- null

Should these be in the App namespace?

Not sure if this is a big issue or not but I thought I would mention it. We are using your spatie/ssl-certificate package which pulls in this package.

We already have an App\Order in our application and as you reference the App namespace inside vendor/spatie/phpunit-snapshot-assertions/example/composer.json my IDE is picking up that we have two classes named the same in the same namespace.

Tests on PHP 7.3 are failing

The tests on PHP 7.3 are failing because the output changed in that version of PHP.

Our tests should assert different output for PHP 7.3 so tests on Travis pass for all tested PHP versions.

image

Empty json objects encoded as arrays

Hi,

When an empty object is used it is been serialised and snapshotted as an array, for example this will fail once the snapshot is created:

    public function testJsonSnapshot()
    {
        $json = json_encode((object) [
            'data' => [
                'objectData' => (object)[]
            ]
        ]);

        // dump($json);
        // {"data":{"objectData":{}}}

        $this->assertMatchesJsonSnapshot($json);
        // snapshot contents
        /*
        {
            "data": {
                "objectData": []
            }
        }
        */

    }

basically [] in the snapshot vs {} in the $json variable

Paul

Config Option to preview new snapshots instead of creating them

Hi there

Awesome library guys. So helpful. Like it a lot!

I'd like to propose a feature request: Disable auto-creating new snapshots and instead print out the content as preview.

Using the "--update-snapshots" one can still update/create those missing snapshots.

Thx for your consideration
Best
Dimitri

Default to `without-creating-snapshots`

Just started using this package yesterday, it's excellent, thanks a lot.
I got the feeling that --without-creating-snapshots should be the default, in the sense that stating that your code is working fine should be a deliberate action and not a given.
I actually got some false positives and had to delete some snapshots.

Just my opinion, the package "as is" is perfectly fine.

PHP object driver

It would be really useful to have a more an advanced PHP object driver that would use all public properties and also checks for existing getters and bool methods aswell (like isActive) similar to how the Symfony serializer works.

Bring back PHP 7.3 support

Hi, thanks for the useful tool, but can i ask to bring back support for PHP 7.3? This PHP version is going to be still supported for almost 2 years, also current phpunit (9.0) supports 7.3
Thanks!

snapshotConstraint proposal

This package is great! I just wanted to share some thoughts.

Inside tests using MatchesSnpashots trait I started adding this handy function.

    public function snapshotConstraint(): Constraint
    {
        return self::callback(function ($argument) {
            $this->assertMatchesJsonSnapshot([$argument]);
            return true;
        });
    }

Within a test case using mocks where I don't care what the parameter value exactly is, but want to make sure it didn't change I do the following

        // Find record in cache
        $this->redisMock->expects(self::once())
            ->method('get')
            // Compare key to key snapshot
            ->with($this->snapshotConstraint())
            ->willReturn(json_encode($data, JSON_THROW_ON_ERROR));

I guess this can be implemented in a better way by defining a new constraint class extending the PHPUnit Constraint.

Feature - Snapshot JSON schema assertion

Hi Freek,

I'm fan of the idea of snapshot testing where the result of a test is used as an assertion the next time the test runs.

The package currently matches on the following options:

  • assertMatchesSnapshot($actual)
  • assertMatchesJsonSnapshot($actual)
  • assertMatchesXmlSnapshot($actual)
  • assertMatchesFileSnapshot($filePath)
  • assertMatchesFileHashSnapshot($filePath)

We use a lot of JSON based API's, so the snapshot assertMatchesJsonSnapshot is suitable for testing the results of the JSON API.

But it requires the JSON response to match every time, which is great for unit tests but less for "integration "test.

I had the idea to use the same manner of taking snapshots for "integration" tests, where the data in the JSON response will change depending on the input you give to the API but the structure / schema of the JSON should be the same.

Example: JSON response

{
  "result": {
    "code": 200,
    "message": "success"
  },
  "parcel": {
    "id": "57d6b6ad88cdf78b461777",
    "alias": "null",
    "name": "Laptop"
  }
}

The name and ID in the response will be different every time we create a parcel.

But the JSON schema should be the same otherwise my test should fail.

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "result": {
      "type": "object",
      "properties": {
        "code": {
          "type": "number",
          "description": "The result code"
        },
        "message": {
          "type": "string",
          "description": "The result message"
        }
      }
    },
    "parcel": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "description": "The unique ID of the Parcel."
        },
        "alias": {
          "type": "string",
          "description": "not used currently"
        },
        "name": {
          "type": "string",
          "description": "The name/description of the parcel,"
        }
      }
    }
  }
}

The test would fail if for example "name" would be an integer

{
  "result": {
    "code": 200,
    "message": "success"
  },
  "parcel": {
    "id": "57d6b6ad88cdf78b461777",
    "alias": "null",
    "name": 1234567890
  }
}

The test would fail if for example the JSON response would not match the snapshot schema, when a new property is added or an expected one is missing

Missing property example

{
  "result": {
    "code": 200,
    "message": "success"
  },
  "parcel": {
    "id": "57d6b6ad88cdf78b461777",
    "alias": "null",
  }
}

New unexpected property example

{
  "result": {
    "code": 200,
    "message": "success"
  },
  "parcel": {
    "id": "57d6b6ad88cdf78b461777",
    "alias": "null",
    "name": "Laptop",
    "code": "4567890KHJL"
  }
}

How it could work

  1. Do API Call
  2. Convert JSON response to JSON schema
  3. Snapshot JSON schema result
  4. Match JSON response with the snapshot JSON schema

Step 1, 3 is something that the current package already supports, more or less
Step 2, 4 would be the new part.

For Step 4: I have found this: https://github.com/estahn/phpunit-json-assertions
Which could be used to validate if the JSON response matches the schema.

For step 2:
This is the big unknow, how would you convert a JSON response and into a JSON schema.

I have found this:
https://github.com/solvire/php-json-schema-generator

Questions

I want to develop this feature to extend the package, but I'm looking for your advice to do it properly, so I'm looking for your advice / guidance.

  • Would this be a valuable add-on for this package?
  • Would the steps be the correct way to implement it?
  • I have looked for something like this not found anything suitable. Perhaps you ran into something that does phpunit JSON schema assertions?
  • Would the available classes by up-to-standard to use?

Looking forward for your input.

Find a way to clean up unused snapshots

This isn't trivial since it's hard to find out which snapshots are unused, since PHPUnit can be run for a specific test class, or with --filter.

Additionally, if #16 is implemented, we need to know more that just the method signature.

Failing on Windows because of new line

All snapshots tests are failing on me on windows, presumably because of new line differences, as snapshots were made on Mac, but I'm running the test on Windows:

  --- Expected
  +++ Actual
  @@ @@
  -'<html lang="en">\r\n
  -<head>\r\n
  +'<html lang="en">\n
  +<head>\n

I'm running
php artisan test --parallel
I have CREATE_SNAPSHOTS=false in .env
and tests/**/__snapshots__/** text eol=lf in .gitattributes

add binary driver for images, videos and other binary files

Hey,

I have some controller actions returning authorization protected binary files (image, video) or runtime generated binary content (image, zip).
It would be cool if there would be a BinaryDriver which accepts the wanted extension as construct argument. So I would be able to do this:

$this->assertMatchesBinarySnapshot($image, 'jpg');

And it matches with Test__test_image__1.jpg snapshot file.

Allow passing options as env var

Hi, I would like to use the package with paratest, which is used under the hood by Laravel for parallel testing.

I don't succeed to pass any parameters like --without-creating-snapshots, and raised an issue: paratestphp/paratest#607

The answer is:

In Paratest the command is passed to PHPUnit directly, not through var:

https://github.com/paratestphp/paratest/blob/82d50dadbc357bc6a24c0a69710b6cae004017d5/bin/phpunit-wrapper.php#L41-L42

I see you are leveraging a non orthodox way to get an option to the PHPUnit extension: the should be reserved for settings.

Yes it would be possible to have working on Paratest too, but I suggest you to ask/PR https://github.com/spatie/phpunit-snapshot-assertions to move away from and rely on environment variables: they are widely used for this purpose (custom flags) and already supported both into configuration and one-time runs.

And actually I'm quite agreeing with this statement: using env var for those parameters doesn't seem to be a bad idea (in addition of current way).

Would you be open to a PR for that?

Thanks.

Add ability to have multiple snapshots per test

Currently if your test has a data provider, or you try to call matchesSnapshot multiple times per test, it doesn't work. This is one of the things I miss from our library, it would be great if this was a supported scenario. Example:

/**
 * @dataProvider provideSomeData
 */
public function testSomething(string $someData)
{
    $this->assertMatchesSnapshot('foo'.$someData);
}

This would fail because the snapshots are named from the test method, so they overwrite each other.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.