Repository: styled-components/babel-plugin-styled-components (js)

Issue:
File hash in displayName is too fragile
Using a hash of the file contents when generating the displayName causes snapshot tests to fail frequently and unnecessarily. Changes to my files that don't effect the component require me to update my snapshots - the false negatives kind of make them redundant.

I assume the reason for the file hash is to make the class name unique, as 2 components could possibly have the same name.

Would it not make more sense, and be less fragile, to use a hash of the file path?

Pull request:
use a simpler hashing algorithm when a file system is available 
fixes #105

By not including the file contents in the hash, the generated IDs
should be more durable and less susceptible to minor differences
in file content due to a babel plugin being applied to ssr vs
client config or something like that

Patch:
diff --git a/.gitignore b/.gitignore
index 45f7222..2c9e6e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 node_modules
 *.log
 lib
+.DS_Store
diff --git a/package.json b/package.json
index 3ae10f7..fef2cda 100644
--- a/package.json
+++ b/package.json
@@ -36,5 +36,10 @@
   "dependencies": {
     "babel-types": "^6.26.0",
     "stylis": "^3.0.0"
+  },
+  "jest": {
+    "snapshotSerializers": [
+      "<rootDir>/test/whitespaceTrimmingSerializer.js"
+    ]
   }
 }
diff --git a/src/visitors/displayNameAndId.js b/src/visitors/displayNameAndId.js
index 33ffc75..1851a57 100644
--- a/src/visitors/displayNameAndId.js
+++ b/src/visitors/displayNameAndId.js
@@ -76,7 +76,15 @@ const getFileHash = (state) => {
   const moduleName = moduleRoot && JSON.parse(fs.readFileSync(path.join(moduleRoot, 'package.json'))).name
   const code = file.code
 
-  const fileHash = hash([moduleName, filePath, code].join(''))
+  const stuffToHash = [moduleName]
+
+  if (filePath) {
+    stuffToHash.push(filePath)
+  } else {
+    stuffToHash.push(code)
+  }
+
+  const fileHash = hash(stuffToHash.join(''))
   file.set(FILE_HASH, fileHash)
   return fileHash
 }


Test patch:
diff --git a/test/__snapshots__/index.test.js.snap b/test/__snapshots__/index.test.js.snap
new file mode 100644
index 0000000..78e993e
--- /dev/null
+++ b/test/__snapshots__/index.test.js.snap
@@ -0,0 +1,313 @@
+exports[`fixtures should add display names 1`] = `
+"const Test = styled.div.withConfig({
+  displayName: \'Test\'
+})\`width:100%;\`;
+const Test2 = styled(\'div\').withConfig({
+  displayName: \'Test2\'
+})\`\`;
+const Test3 = true ? styled.div.withConfig({
+  displayName: \'Test3\'
+})\`\` : styled.div.withConfig({
+  displayName: \'Test3\'
+})\`\`;
+const styles = { One: styled.div.withConfig({
+    displayName: \'One\'
+  })\`\` };
+let Component;
+Component = styled.div.withConfig({
+  displayName: \'Component\'
+})\`\`;
+const WrappedComponent = styled(Inner).withConfig({
+  displayName: \'WrappedComponent\'
+})\`\`;"
+`;
+
+exports[`fixtures should add identifier 1`] = `
+"const Test = styled.div.withConfig({
+  componentId: \"iiazu4-0\"
+})\`width:100%;\`;
+const Test2 = true ? styled.div.withConfig({
+  componentId: \"iiazu4-1\"
+})\`\` : styled.div.withConfig({
+  componentId: \"iiazu4-2\"
+})\`\`;
+const styles = { One: styled.div.withConfig({
+    componentId: \"iiazu4-3\"
+  })\`\` };
+let Component;
+Component = styled.div.withConfig({
+  componentId: \"iiazu4-4\"
+})\`\`;
+const WrappedComponent = styled(Inner).withConfig({
+  componentId: \"iiazu4-5\"
+})\`\`;"
+`;
+
+exports[`fixtures should add identifier and display name 1`] = `
+"const Test = styled.div.withConfig({
+  displayName: \"Test\",
+  componentId: \"s1vkcy8r-0\"
+})\`width:100%;\`;
+const Test2 = true ? styled.div.withConfig({
+  displayName: \"Test2\",
+  componentId: \"s1vkcy8r-1\"
+})\`\` : styled.div.withConfig({
+  displayName: \"Test2\",
+  componentId: \"s1vkcy8r-2\"
+})\`\`;
+const styles = { One: styled.div.withConfig({
+    displayName: \"One\",
+    componentId: \"s1vkcy8r-3\"
+  })\`\` };
+let Component;
+Component = styled.div.withConfig({
+  displayName: \"Component\",
+  componentId: \"s1vkcy8r-4\"
+})\`\`;
+const WrappedComponent = styled(Inner).withConfig({
+  displayName: \"WrappedComponent\",
+  componentId: \"s1vkcy8r-5\"
+})\`\`;"
+`;
+
+exports[`fixtures should allow chains of member calls 1`] = `
+"const WithAttrs = styled.div.attrs({ some: \'value\' }).withConfig({
+  displayName: \'WithAttrs\'
+})\`\`;
+const WithAttrsWrapped = styled(Inner).attrs({ some: \'value\' }).withConfig({
+  displayName: \'WithAttrsWrapped\'
+})\`\`;"
+`;
+
+exports[`fixtures should minify css in helpers 1`] = `
+"import { css, keyframes } from \'styled-components\';
+
+const key = keyframes\`to{transform:rotate(360deg);}\`;
+
+const color = css\`color:\${theColor};\`;"
+`;
+
+exports[`fixtures should minify css to use with transpilation 1`] = `
+"\'use strict\';
+
+var _templateObject = _taggedTemplateLiteral([\'width:100%;\'], [\'width:100%;\']),
+    _templateObject2 = _taggedTemplateLiteral([\'content:\"  \', \'  \";\'], [\'content:\"  \', \'  \";\']),
+    _templateObject3 = _taggedTemplateLiteral([\'content:\"  \', \'  \";color:red;\'], [\'content:\"  \', \'  \";color:red;\']),
+    _templateObject4 = _taggedTemplateLiteral([\'color:red;\'], [\'color:red;\']),
+    _templateObject5 = _taggedTemplateLiteral([\'&:hover{color:blue;}\'], [\'&:hover{color:blue;}\']);
+
+var _styledComponents = require(\'styled-components\');
+
+var _styledComponents2 = _interopRequireDefault(_styledComponents);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
+
+var Simple = _styledComponents2.default.div(_templateObject);
+
+var Interpolation = _styledComponents2.default.div(_templateObject2, function (props) {
+  return props.text;
+});
+
+var SpecialCharacters = _styledComponents2.default.div(_templateObject3, function (props) {
+  return props.text;
+});
+
+var Comment = _styledComponents2.default.div(_templateObject4);
+
+var Parens = _styledComponents2.default.div(_templateObject5);"
+`;
+
+exports[`fixtures should minify css to use without transpilation 1`] = `
+"import styled from \'styled-components\';
+
+const Simple = styled.div\`width:100%;\`;
+
+const Interpolation = styled.div\`content:\"https://test.com/\${props => props.endpoint}\";\`;
+
+const SpecialCharacters = styled.div\`content:\"  \${props => props.text}  \";color:red;\`;
+
+const Comment = styled.div\`width:100%;color:red;\`;
+
+const Parens = styled.div\`&:hover{color:blue;}color:red;\`;
+
+const UrlComments = styled.div\`color:red;background:red;border:1px solid green;\`;"
+`;
+
+exports[`fixtures should not preprocess import 1`] = `
+"import styled from \'styled-components\';
+require(\'styled-components\');"
+`;
+
+exports[`fixtures should not use private api if not required 1`] = `
+"import styled from \'styled-components\';
+
+const Test = styled.div\`width:100%;\`;"
+`;
+
+exports[`fixtures should preprocess css 1`] = `
+"const Simple = styled.div.withConfig({
+  displayName: \"before__Simple\"
+})([[\"{width:100%;}\"]]);
+
+const Nested = styled.div.withConfig({
+  displayName: \"before__Nested\"
+})([[\"{width:100%;}\"], [\":hover{color:papayawhip;}\"], [\" > div{background:white;}\"]]);
+
+const Interpolations = styled.div.withConfig({
+  displayName: \"before__Interpolations\"
+})([[\"{width:\", props => props.width, \";}\"]]);
+
+const NestedAndInterpolations = styled.div.withConfig({
+  displayName: \"before__NestedAndInterpolations\"
+})([[\"{width:\", props => props.width, \";}\"], [\":hover{color:\", props => props.color, \";}\"]]);
+
+const SelectorInterpolation = styled.div.withConfig({
+  displayName: \"before__SelectorInterpolation\"
+})([[\"{width:\", props => props.width, \";}\"], [\" \", props => props.selector, \"{color:papayawhip;}\"]]);
+
+const RulesetInterpolationA = styled.div.withConfig({
+  displayName: \"before__RulesetInterpolationA\"
+})([[\"{width:\", props => props.width, \";\", props => props.ruleset, \";}\"], [\":hover{color:papayawhip;}\"]]);
+
+const RulesetInterpolationB = styled.div.withConfig({
+  displayName: \"before__RulesetInterpolationB\"
+})([[\"{\", props => props.ruleset, \";width:\", props => props.width, \";}\"], [\":hover{color:papayawhip;}\"]]);
+
+const Prefixes = styled.div.withConfig({
+  displayName: \"before__Prefixes\"
+})([[\"{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}\"]]);
+
+const DoubleInterpolation = styled.div.withConfig({
+  displayName: \"before__DoubleInterpolation\"
+})([[\"{margin:\", props => props.vert, \" \", props => props.hori, \";}\"]]);"
+`;
+
+exports[`fixtures should preprocess import 1`] = `
+"import styled from \'styled-components/no-parser\';
+require(\'styled-components/no-parser\');"
+`;
+
+exports[`fixtures should preprocess inject global 1`] = `"injectGlobal([[\"\", glob, \"\\n/*! preserve comment */html,body{margin:100000px;padding:\", test, \";}@media (max-width:999px){html,body{margin:0;}}.root{width:100%;}\"]]);"`;
+
+exports[`fixtures should preprocess keyframes 1`] = `"const Animation = keyframes([[\"@-webkit-keyframes \"], [\"{0%{opacity:0;}100%{opacity:1;}}@keyframes \"], [\"{0%{opacity:0;}100%{opacity:1;}}\"]]);"`;
+
+exports[`fixtures should track the imported variable 1`] = `
+"import s from \"styled-components\";
+
+const Test = s.div.withConfig({
+  displayName: \"Test\",
+  componentId: \"s13i891w-0\"
+})\`width:100%;\`;
+const Test2 = true ? s.div.withConfig({
+  displayName: \"Test2\",
+  componentId: \"s13i891w-1\"
+})\`\` : s.div.withConfig({
+  displayName: \"Test2\",
+  componentId: \"s13i891w-2\"
+})\`\`;
+const styles = { One: s.div.withConfig({
+    displayName: \"One\",
+    componentId: \"s13i891w-3\"
+  })\`\` };
+let Component;
+Component = s.div.withConfig({
+  displayName: \"Component\",
+  componentId: \"s13i891w-4\"
+})\`\`;
+const WrappedComponent = s(Inner).withConfig({
+  displayName: \"WrappedComponent\",
+  componentId: \"s13i891w-5\"
+})\`\`;"
+`;
+
+exports[`fixtures should transpile require default 1`] = `
+"const styled_default = require(\"styled-components/no-parser\");
+
+const TestNormal = styled.div.withConfig({
+  displayName: \"before__TestNormal\",
+  componentId: \"iqyhdm-0\"
+})([[\"{width:100%;}\"]]);
+
+const Test = styled_default.default.div.withConfig({
+  displayName: \"before__Test\",
+  componentId: \"iqyhdm-1\"
+})([[\"{width:100%;}\"]]);
+
+const TestCallExpression = styled_default.default(Test).withConfig({
+  displayName: \"before__TestCallExpression\",
+  componentId: \"iqyhdm-2\"
+})([[\"{height:20px;}\"]]);"
+`;
+
+exports[`fixtures should transpile template literals with config 1`] = `
+"\'use strict\';
+
+var _styledComponents = require(\'styled-components\');
+
+var _styledComponents2 = _interopRequireDefault(_styledComponents);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Named = _styledComponents2.default.div.withConfig({
+  displayName: \'before__Named\'
+})([\'\\n  width: 100%;\\n\']);
+
+var NamedWithInterpolation = _styledComponents2.default.div.withConfig({
+  displayName: \'before__NamedWithInterpolation\'
+})([\'\\n  color: \', \';\\n\'], function (color) {
+  return props.color;
+});
+
+var Wrapped = (0, _styledComponents2.default)(Inner).withConfig({
+  displayName: \'before__Wrapped\'
+})([\'color: red;\']);"
+`;
+
+exports[`fixtures should transpile template literals without config 1`] = `
+"\'use strict\';
+
+var _styledComponents = require(\'styled-components\');
+
+var _styledComponents2 = _interopRequireDefault(_styledComponents);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+var Named = _styledComponents2.default.div([\'\\n  width: 100%;\\n\']);
+
+var NamedWithInterpolation = _styledComponents2.default.div([\'\\n  color: \', \';\\n\'], function (color) {
+  return props.color;
+});
+
+var Wrapped = (0, _styledComponents2.default)(Inner)([\'color: red;\']);"
+`;
+
+exports[`fixtures should use file name 1`] = `
+"import styled from \"styled-components\";
+
+const Test = styled.div.withConfig({
+  displayName: \"before__Test\",
+  componentId: \"s1tyeg9p-0\"
+})\`color:red;\`;
+const before = styled.div.withConfig({
+  displayName: \"before\",
+  componentId: \"s1tyeg9p-1\"
+})\`color:blue;\`;
+styled.div.withConfig({
+  displayName: \"before\",
+  componentId: \"s1tyeg9p-2\"
+})\`\`;
+export default styled.button.withConfig({
+  displayName: \"before\",
+  componentId: \"s1tyeg9p-3\"
+})\`\`;"
+`;
+
+exports[`fixtures should work with hoisted default as import 1`] = `
+"const Test = s.div.withConfig({
+  displayName: \'before__Test\',
+  componentId: \'s1vk20b3-0\'
+})\`width:100%;\`;
+import { default as s, css } from \'styled-components\';"
+`;
diff --git a/test/fixtures/01-add-display-names/after.js b/test/fixtures/01-add-display-names/after.js
deleted file mode 100644
index 8e8a26a..0000000
--- a/test/fixtures/01-add-display-names/after.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const Test = styled.div.withConfig({
-  displayName: 'Test'
-})`width:100%;`;
-const Test2 = styled('div').withConfig({
-  displayName: 'Test2'
-})``;
-const Test3 = true ? styled.div.withConfig({
-  displayName: 'Test3'
-})`` : styled.div.withConfig({
-  displayName: 'Test3'
-})``;
-const styles = { One: styled.div.withConfig({
-    displayName: 'One'
-  })`` };
-let Component;
-Component = styled.div.withConfig({
-  displayName: 'Component'
-})``;
-const WrappedComponent = styled(Inner).withConfig({
-  displayName: 'WrappedComponent'
-})``;
diff --git a/test/fixtures/02-add-identifier/after.js b/test/fixtures/02-add-identifier/after.js
deleted file mode 100644
index 3750c09..0000000
--- a/test/fixtures/02-add-identifier/after.js
+++ /dev/null
@@ -1,18 +0,0 @@
-const Test = styled.div.withConfig({
-  componentId: "f15zqz-0"
-})`width:100%;`;
-const Test2 = true ? styled.div.withConfig({
-  componentId: "f15zqz-1"
-})`` : styled.div.withConfig({
-  componentId: "f15zqz-2"
-})``;
-const styles = { One: styled.div.withConfig({
-    componentId: "f15zqz-3"
-  })`` };
-let Component;
-Component = styled.div.withConfig({
-  componentId: "f15zqz-4"
-})``;
-const WrappedComponent = styled(Inner).withConfig({
-  componentId: "f15zqz-5"
-})``;
diff --git a/test/fixtures/03-add-identifier-and-display-name/after.js b/test/fixtures/03-add-identifier-and-display-name/after.js
deleted file mode 100644
index 90a8f7f..0000000
--- a/test/fixtures/03-add-identifier-and-display-name/after.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const Test = styled.div.withConfig({
-  displayName: "Test",
-  componentId: "s1j54k93-0"
-})`width:100%;`;
-const Test2 = true ? styled.div.withConfig({
-  displayName: "Test2",
-  componentId: "s1j54k93-1"
-})`` : styled.div.withConfig({
-  displayName: "Test2",
-  componentId: "s1j54k93-2"
-})``;
-const styles = { One: styled.div.withConfig({
-    displayName: "One",
-    componentId: "s1j54k93-3"
-  })`` };
-let Component;
-Component = styled.div.withConfig({
-  displayName: "Component",
-  componentId: "s1j54k93-4"
-})``;
-const WrappedComponent = styled(Inner).withConfig({
-  displayName: "WrappedComponent",
-  componentId: "s1j54k93-5"
-})``;
diff --git a/test/fixtures/04-track-the-imported-variable/after.js b/test/fixtures/04-track-the-imported-variable/after.js
deleted file mode 100644
index 9b0f4f8..0000000
--- a/test/fixtures/04-track-the-imported-variable/after.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import s from "styled-components";
-
-const Test = s.div.withConfig({
-  displayName: "Test",
-  componentId: "syqrfc-0"
-})`width:100%;`;
-const Test2 = true ? s.div.withConfig({
-  displayName: "Test2",
-  componentId: "syqrfc-1"
-})`` : s.div.withConfig({
-  displayName: "Test2",
-  componentId: "syqrfc-2"
-})``;
-const styles = { One: s.div.withConfig({
-    displayName: "One",
-    componentId: "syqrfc-3"
-  })`` };
-let Component;
-Component = s.div.withConfig({
-  displayName: "Component",
-  componentId: "syqrfc-4"
-})``;
-const WrappedComponent = s(Inner).withConfig({
-  displayName: "WrappedComponent",
-  componentId: "syqrfc-5"
-})``;
diff --git a/test/fixtures/05-use-file-name/after.js b/test/fixtures/05-use-file-name/after.js
deleted file mode 100644
index 3f7ca65..0000000
--- a/test/fixtures/05-use-file-name/after.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import styled from "styled-components";
-
-const Test = styled.div.withConfig({
-  displayName: "before__Test",
-  componentId: "s1x8c0g0-0"
-})`color:red;`;
-const before = styled.div.withConfig({
-  displayName: "before",
-  componentId: "s1x8c0g0-1"
-})`color:blue;`;
-styled.div.withConfig({
-  displayName: "before",
-  componentId: "s1x8c0g0-2"
-})``;
-export default styled.button.withConfig({
-  displayName: "before",
-  componentId: "s1x8c0g0-3"
-})``;
diff --git a/test/fixtures/06-work-with-hoisted-default-as-import/after.js b/test/fixtures/06-work-with-hoisted-default-as-import/after.js
deleted file mode 100644
index 0d28270..0000000
--- a/test/fixtures/06-work-with-hoisted-default-as-import/after.js
+++ /dev/null
@@ -1,5 +0,0 @@
-const Test = s.div.withConfig({
-  displayName: 'before__Test',
-  componentId: 's2wuo7o-0'
-})`width:100%;`;
-import { default as s, css } from 'styled-components';
diff --git a/test/fixtures/07-not-use-private-api-if-not-required/after.js b/test/fixtures/07-not-use-private-api-if-not-required/after.js
deleted file mode 100644
index 08a20d6..0000000
--- a/test/fixtures/07-not-use-private-api-if-not-required/after.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import styled from 'styled-components';
-
-const Test = styled.div`width:100%;`;
diff --git a/test/fixtures/08-minify-css-to-use-with-transpilation/after.js b/test/fixtures/08-minify-css-to-use-with-transpilation/after.js
deleted file mode 100644
index 33208c6..0000000
--- a/test/fixtures/08-minify-css-to-use-with-transpilation/after.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict';
-
-var _templateObject = _taggedTemplateLiteral(['width:100%;'], ['width:100%;']),
-    _templateObject2 = _taggedTemplateLiteral(['content:"  ', '  ";'], ['content:"  ', '  ";']),
-    _templateObject3 = _taggedTemplateLiteral(['content:"  ', '  ";color:red;'], ['content:"  ', '  ";color:red;']),
-    _templateObject4 = _taggedTemplateLiteral(['color:red;'], ['color:red;']),
-    _templateObject5 = _taggedTemplateLiteral(['&:hover{color:blue;}'], ['&:hover{color:blue;}']);
-
-var _styledComponents = require('styled-components');
-
-var _styledComponents2 = _interopRequireDefault(_styledComponents);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
-
-var Simple = _styledComponents2.default.div(_templateObject);
-
-var Interpolation = _styledComponents2.default.div(_templateObject2, function (props) {
-  return props.text;
-});
-
-var SpecialCharacters = _styledComponents2.default.div(_templateObject3, function (props) {
-  return props.text;
-});
-
-var Comment = _styledComponents2.default.div(_templateObject4);
-
-var Parens = _styledComponents2.default.div(_templateObject5);
diff --git a/test/fixtures/09-minify-css-to-use-without-transpilation/after.js b/test/fixtures/09-minify-css-to-use-without-transpilation/after.js
deleted file mode 100644
index 63bdade..0000000
--- a/test/fixtures/09-minify-css-to-use-without-transpilation/after.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import styled from 'styled-components';
-
-const Simple = styled.div`width:100%;`;
-
-const Interpolation = styled.div`content:"https://test.com/${props => props.endpoint}";`;
-
-const SpecialCharacters = styled.div`content:"  ${props => props.text}  ";color:red;`;
-
-const Comment = styled.div`width:100%;color:red;`;
-
-const Parens = styled.div`&:hover{color:blue;}color:red;`;
-
-const UrlComments = styled.div`color:red;background:red;border:1px solid green;`;
diff --git a/test/fixtures/10-minify-css-in-helpers/after.js b/test/fixtures/10-minify-css-in-helpers/after.js
deleted file mode 100644
index 0a99a02..0000000
--- a/test/fixtures/10-minify-css-in-helpers/after.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import { css, keyframes } from 'styled-components';
-
-const key = keyframes`to{transform:rotate(360deg);}`;
-
-const color = css`color:${theColor};`;
diff --git a/test/fixtures/11-transpile-template-literals-with-config/after.js b/test/fixtures/11-transpile-template-literals-with-config/after.js
deleted file mode 100644
index 3bf6e6f..0000000
--- a/test/fixtures/11-transpile-template-literals-with-config/after.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-var _styledComponents = require('styled-components');
-
-var _styledComponents2 = _interopRequireDefault(_styledComponents);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var Named = _styledComponents2.default.div.withConfig({
-  displayName: 'before__Named'
-})(['\n  width: 100%;\n']);
-
-var NamedWithInterpolation = _styledComponents2.default.div.withConfig({
-  displayName: 'before__NamedWithInterpolation'
-})(['\n  color: ', ';\n'], function (color) {
-  return props.color;
-});
-
-var Wrapped = (0, _styledComponents2.default)(Inner).withConfig({
-  displayName: 'before__Wrapped'
-})(['color: red;']);
diff --git a/test/fixtures/12-transpile-template-literals-without-config/after.js b/test/fixtures/12-transpile-template-literals-without-config/after.js
deleted file mode 100644
index 72a8ff1..0000000
--- a/test/fixtures/12-transpile-template-literals-without-config/after.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-var _styledComponents = require('styled-components');
-
-var _styledComponents2 = _interopRequireDefault(_styledComponents);
-
-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
-
-var Named = _styledComponents2.default.div(['\n  width: 100%;\n']);
-
-var NamedWithInterpolation = _styledComponents2.default.div(['\n  color: ', ';\n'], function (color) {
-  return props.color;
-});
-
-var Wrapped = (0, _styledComponents2.default)(Inner)(['color: red;']);
diff --git a/test/fixtures/13-allow-chains-of-member-calls/after.js b/test/fixtures/13-allow-chains-of-member-calls/after.js
deleted file mode 100644
index ddfc01a..0000000
--- a/test/fixtures/13-allow-chains-of-member-calls/after.js
+++ /dev/null
@@ -1,6 +0,0 @@
-const WithAttrs = styled.div.attrs({ some: 'value' }).withConfig({
-  displayName: 'WithAttrs'
-})``;
-const WithAttrsWrapped = styled(Inner).attrs({ some: 'value' }).withConfig({
-  displayName: 'WithAttrsWrapped'
-})``;
diff --git a/test/fixtures/14-preprocess-css/after.js b/test/fixtures/14-preprocess-css/after.js
deleted file mode 100644
index 0c5f6ca..0000000
--- a/test/fixtures/14-preprocess-css/after.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const Simple = styled.div.withConfig({
-  displayName: "before__Simple"
-})([["{width:100%;}"]]);
-
-const Nested = styled.div.withConfig({
-  displayName: "before__Nested"
-})([["{width:100%;}"], [":hover{color:papayawhip;}"], [" > div{background:white;}"]]);
-
-const Interpolations = styled.div.withConfig({
-  displayName: "before__Interpolations"
-})([["{width:", props => props.width, ";}"]]);
-
-const NestedAndInterpolations = styled.div.withConfig({
-  displayName: "before__NestedAndInterpolations"
-})([["{width:", props => props.width, ";}"], [":hover{color:", props => props.color, ";}"]]);
-
-const SelectorInterpolation = styled.div.withConfig({
-  displayName: "before__SelectorInterpolation"
-})([["{width:", props => props.width, ";}"], [" ", props => props.selector, "{color:papayawhip;}"]]);
-
-const RulesetInterpolationA = styled.div.withConfig({
-  displayName: "before__RulesetInterpolationA"
-})([["{width:", props => props.width, ";", props => props.ruleset, ";}"], [":hover{color:papayawhip;}"]]);
-
-const RulesetInterpolationB = styled.div.withConfig({
-  displayName: "before__RulesetInterpolationB"
-})([["{", props => props.ruleset, ";width:", props => props.width, ";}"], [":hover{color:papayawhip;}"]]);
-
-const Prefixes = styled.div.withConfig({
-  displayName: "before__Prefixes"
-})([["{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}"]]);
-
-const DoubleInterpolation = styled.div.withConfig({
-  displayName: "before__DoubleInterpolation"
-})([["{margin:", props => props.vert, " ", props => props.hori, ";}"]]);
diff --git a/test/fixtures/15-preprocess-keyframes/after.js b/test/fixtures/15-preprocess-keyframes/after.js
deleted file mode 100644
index fabc177..0000000
--- a/test/fixtures/15-preprocess-keyframes/after.js
+++ /dev/null
@@ -1,1 +0,0 @@
-const Animation = keyframes([["@-webkit-keyframes "], ["{0%{opacity:0;}100%{opacity:1;}}@keyframes "], ["{0%{opacity:0;}100%{opacity:1;}}"]]);
\ No newline at end of file
diff --git a/test/fixtures/16-not-preprocess-import/after.js b/test/fixtures/16-not-preprocess-import/after.js
deleted file mode 100644
index ecec044..0000000
--- a/test/fixtures/16-not-preprocess-import/after.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import styled from 'styled-components';
-require('styled-components');
diff --git a/test/fixtures/17-preprocess-import/after.js b/test/fixtures/17-preprocess-import/after.js
deleted file mode 100644
index a7e9224..0000000
--- a/test/fixtures/17-preprocess-import/after.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import styled from 'styled-components/no-parser';
-require('styled-components/no-parser');
diff --git a/test/fixtures/18-preprocess-inject-global/after.js b/test/fixtures/18-preprocess-inject-global/after.js
deleted file mode 100644
index 7d6151a..0000000
--- a/test/fixtures/18-preprocess-inject-global/after.js
+++ /dev/null
@@ -1,1 +0,0 @@
-injectGlobal([["", glob, "\n/*! preserve comment */html,body{margin:100000px;padding:", test, ";}@media (max-width:999px){html,body{margin:0;}}.root{width:100%;}"]]);
diff --git a/test/fixtures/19-transpile-require-default/after.js b/test/fixtures/19-transpile-require-default/after.js
deleted file mode 100644
index d81fd8d..0000000
--- a/test/fixtures/19-transpile-require-default/after.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const styled_default = require("styled-components/no-parser");
-
-const TestNormal = styled.div.withConfig({
-  displayName: "before__TestNormal",
-  componentId: "y0c69d-0"
-})([["{width:100%;}"]]);
-
-const Test = styled_default.default.div.withConfig({
-  displayName: "before__Test",
-  componentId: "y0c69d-1"
-})([["{width:100%;}"]]);
-
-const TestCallExpression = styled_default.default(Test).withConfig({
-  displayName: "before__TestCallExpression",
-  componentId: "y0c69d-2"
-})([["{height:20px;}"]]);
diff --git a/test/index.test.js b/test/index.test.js
index f1cbbd5..544a784 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -3,24 +3,16 @@ import fs from 'fs'
 import { transformFileSync } from 'babel-core'
 import plugin from '../src'
 
-function trim(str) {
-  return str.replace(/^\s+|\s+$/, '');
-}
-
 describe('fixtures', () => {
   const fixturesDir = path.join(__dirname, 'fixtures')
   fs.readdirSync(fixturesDir).sort().map((caseName) => {
     if (caseName === '.DS_Store') return
     it(`should ${caseName.replace(/^\d*-/, '').split('-').join(' ')}`, () => {
       const fixtureDir = path.join(fixturesDir, caseName)
-      const beforePath = path.join(fixtureDir, 'before.js');
-      const before = transformFileSync(beforePath).code
-
-      const after = fs.readFileSync(
-          path.join(fixtureDir, 'after.js')
-      ).toString()
+      const fixturePath = path.join(fixtureDir, 'before.js');
+      const fixture = transformFileSync(fixturePath).code
 
-      expect(trim(before)).toEqual(trim(after))
+      expect(fixture).toMatchSnapshot()
     })
   })
 })
diff --git a/test/whitespaceTrimmingSerializer.js b/test/whitespaceTrimmingSerializer.js
new file mode 100644
index 0000000..a866dce
--- /dev/null
+++ b/test/whitespaceTrimmingSerializer.js
@@ -0,0 +1,9 @@
+const trailingWhitespaceRegex = /^\s+|\s+$/;
+
+module.exports = {
+    print(val, serialize, indent) {
+      return serialize(val.replace(trailingWhitespaceRegex, ''));
+    },
+
+    test(val) { return trailingWhitespaceRegex.test(val); },
+};