<commit_before>import React, { Component } from 'react'
import { shallow, mount } from 'enzyme'

import { resetStyled, expectCSSMatches } from './utils'
import StyleSheet, { SC_ATTR, LOCAL_ATTR } from '../models/BrowserStyleSheet'

import _injectGlobal from '../constructors/injectGlobal'
import stringifyRules from '../utils/stringifyRules'
import css from '../constructors/css'
const injectGlobal = _injectGlobal(stringifyRules, css)

let styled

const getStyleTags = () => (
  Array.from(document.querySelectorAll('style')).map(el => ({
    isLocal: el.getAttribute('data-styled-components-is-local'),
    css: el.innerHTML.trim().replace(/\s+/mg, ' ')
  }))
)

describe('ssr', () => {
  /**
   * Make sure the setup is the same for every test
   */
  beforeEach(() => {
    styled = resetStyled()
  })

  describe('rehydtration with existing styled components', () => {
    beforeEach(() => {
      /* Hash 1323611362 is based on name TWO and contents color: red.
       * Change either and this will break. */
      document.head.innerHTML = `
        <style ${SC_ATTR}='b' ${LOCAL_ATTR}='true'>
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should preserve the styles', () => {
      expectCSSMatches('.TWO {} .b { color: red; }')
    })

    it('should append a new component like normal', () => {
      const Comp = styled.div.withConfig({ componentId: 'ONE' })`
        color: blue;
      `
      shallow(<Comp />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; }')
    })

    it('should reuse a componentId', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })``
      shallow(<B />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; }')
    })

    it('should reuse a componentId and generated class', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; }')
    })

    it('should reuse a componentId and inject new classes', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      const C = styled.div.withConfig({ componentId: 'TWO' })`color: green;`
      shallow(<C />)
      expectCSSMatches('.TWO {} .b { color: red; } .c { color: green; } .ONE { } .a { color: blue; }')
    })
  })

  describe('with other rendered styles', () => {
    beforeEach(() => {
      /* Same css as before, but without the data attributes we ignore it */
      document.head.innerHTML = `
        <style>
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should leave the existing styles there', () => {
      expectCSSMatches('.TWO {} .b { color: red; }')
    })

    it('should generate new classes, even if they have the same name', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; } .TWO {} .b { color: red; } ')
    })
  })

  describe('with global styles', () => {
    beforeEach(() => {
      /* Adding a non-local stylesheet with a hash 557410406 which is
       * derived from "body { background: papayawhip; }" so be careful
       * changing it. */
      document.head.innerHTML = `
        <style ${SC_ATTR} ${LOCAL_ATTR}='false'>
          /* sc-component-id: sc-global-557410406 */
          body { background: papayawhip; }
        </style>
        <style ${SC_ATTR}='b' ${LOCAL_ATTR}='true'>
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should leave the existing styles there', () => {
      expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; }')
    })

    it('should inject new global styles at the end', () => {
      injectGlobal`
        body { color: tomato; }
      `
      expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; } body { color: tomato; }')

      expect(getStyleTags()).toEqual([
        { isLocal: 'false', css: '/* sc-component-id: sc-global-557410406 */ body { background: papayawhip; }', },
        { isLocal: 'true', css: '/* sc-component-id: TWO */ .TWO {} .b { color: red; }', },
        { isLocal: 'false', css: '/* sc-component-id: sc-global-2299393384 */ body {color: tomato;}', },
      ])
    })

    it('should interleave global and local styles', () => {
      injectGlobal`
        body { color: tomato; }
      `
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)

      expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; } body { color: tomato; } .ONE { } .a { color: blue; }')
      expect(getStyleTags()).toEqual([
        { isLocal: 'false', css: '/* sc-component-id: sc-global-557410406 */ body { background: papayawhip; }', },
        { isLocal: 'true', css: '/* sc-component-id: TWO */ .TWO {} .b { color: red; }', },
        { isLocal: 'false', css: '/* sc-component-id: sc-global-2299393384 */ body {color: tomato;}', },
        { isLocal: 'true', css: '/* sc-component-id: ONE */ .ONE {} .a {color: blue;}', },
      ])
    })
  })

  describe('with all styles already rendered', () => {
    beforeEach(() => {
      document.head.innerHTML = `
        <style ${SC_ATTR} ${LOCAL_ATTR}='false'>
           /* sc-component-id: sc-global-1455077013 */
          html { font-size: 16px; }
           /* sc-component-id: sc-global-557410406 */
          body { background: papayawhip; }
        </style>
        <style ${SC_ATTR}='a b' ${LOCAL_ATTR}='true'>
          /* sc-component-id: ONE */
          .ONE {}
          .a { color: blue; }
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should not touch existing styles', () => {
      expectCSSMatches(`
        html { font-size: 16px; }
        body { background: papayawhip; }
        .ONE { } .a { color: blue; }
        .TWO { } .b { color: red; }
      `)
    })

    it('should leave styles if rendered in the same order they were created with', () => {
      injectGlobal`
        html { font-size: 16px; }
      `<commit_msg>important test-showing that the style tags in the dom don't get touched until something has to change<commit_after>import React, { Component } from 'react'
import { shallow, mount } from 'enzyme'

import { resetStyled, expectCSSMatches } from './utils'
import StyleSheet, { SC_ATTR, LOCAL_ATTR } from '../models/BrowserStyleSheet'

import _injectGlobal from '../constructors/injectGlobal'
import stringifyRules from '../utils/stringifyRules'
import css from '../constructors/css'
const injectGlobal = _injectGlobal(stringifyRules, css)

let styled

const getStyleTags = () => (
  Array.from(document.querySelectorAll('style')).map(el => ({
    isLocal: el.getAttribute('data-styled-components-is-local'),
    css: el.innerHTML.trim().replace(/\s+/mg, ' ')
  }))
)

describe('ssr', () => {
  /**
   * Make sure the setup is the same for every test
   */
  beforeEach(() => {
    styled = resetStyled()
  })

  describe('rehydtration with existing styled components', () => {
    beforeEach(() => {
      /* Hash 1323611362 is based on name TWO and contents color: red.
       * Change either and this will break. */
      document.head.innerHTML = `
        <style ${SC_ATTR}='b' ${LOCAL_ATTR}='true'>
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should preserve the styles', () => {
      expectCSSMatches('.TWO {} .b { color: red; }')
    })

    it('should append a new component like normal', () => {
      const Comp = styled.div.withConfig({ componentId: 'ONE' })`
        color: blue;
      `
      shallow(<Comp />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; }')
    })

    it('should reuse a componentId', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })``
      shallow(<B />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; }')
    })

    it('should reuse a componentId and generated class', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; }')
    })

    it('should reuse a componentId and inject new classes', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      const C = styled.div.withConfig({ componentId: 'TWO' })`color: green;`
      shallow(<C />)
      expectCSSMatches('.TWO {} .b { color: red; } .c { color: green; } .ONE { } .a { color: blue; }')
    })
  })

  describe('with other rendered styles', () => {
    beforeEach(() => {
      /* Same css as before, but without the data attributes we ignore it */
      document.head.innerHTML = `
        <style>
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should leave the existing styles there', () => {
      expectCSSMatches('.TWO {} .b { color: red; }')
    })

    it('should generate new classes, even if they have the same name', () => {
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      expectCSSMatches('.TWO {} .b { color: red; } .ONE { } .a { color: blue; } .TWO {} .b { color: red; } ')
    })
  })

  describe('with global styles', () => {
    beforeEach(() => {
      /* Adding a non-local stylesheet with a hash 557410406 which is
       * derived from "body { background: papayawhip; }" so be careful
       * changing it. */
      document.head.innerHTML = `
        <style ${SC_ATTR} ${LOCAL_ATTR}='false'>
          /* sc-component-id: sc-global-557410406 */
          body { background: papayawhip; }
        </style>
        <style ${SC_ATTR}='b' ${LOCAL_ATTR}='true'>
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      StyleSheet.reset()
    })

    it('should leave the existing styles there', () => {
      expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; }')
    })

    it('should inject new global styles at the end', () => {
      injectGlobal`
        body { color: tomato; }
      `
      expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; } body { color: tomato; }')

      expect(getStyleTags()).toEqual([
        { isLocal: 'false', css: '/* sc-component-id: sc-global-557410406 */ body { background: papayawhip; }', },
        { isLocal: 'true', css: '/* sc-component-id: TWO */ .TWO {} .b { color: red; }', },
        { isLocal: 'false', css: '/* sc-component-id: sc-global-2299393384 */ body {color: tomato;}', },
      ])
    })

    it('should interleave global and local styles', () => {
      injectGlobal`
        body { color: tomato; }
      `
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)

      expectCSSMatches('body { background: papayawhip; } .TWO {} .b { color: red; } body { color: tomato; } .ONE { } .a { color: blue; }')
      expect(getStyleTags()).toEqual([
        { isLocal: 'false', css: '/* sc-component-id: sc-global-557410406 */ body { background: papayawhip; }', },
        { isLocal: 'true', css: '/* sc-component-id: TWO */ .TWO {} .b { color: red; }', },
        { isLocal: 'false', css: '/* sc-component-id: sc-global-2299393384 */ body {color: tomato;}', },
        { isLocal: 'true', css: '/* sc-component-id: ONE */ .ONE {} .a {color: blue;}', },
      ])
    })
  })

  describe('with all styles already rendered', () => {
    let styleTags
    beforeEach(() => {
      document.head.innerHTML = `
        <style ${SC_ATTR} ${LOCAL_ATTR}='false'>
           /* sc-component-id: sc-global-1455077013 */
          html { font-size: 16px; }
           /* sc-component-id: sc-global-557410406 */
          body { background: papayawhip; }
        </style>
        <style ${SC_ATTR}='a b' ${LOCAL_ATTR}='true'>
          /* sc-component-id: ONE */
          .ONE {}
          .a { color: blue; }
          /* sc-component-id: TWO */
          .TWO {}
          .b { color: red; }
        </style>
      `
      styleTags = Array.from(document.querySelectorAll('style'))
      StyleSheet.reset()
    })

    it('should not touch existing styles', () => {
      expectCSSMatches(`
        html { font-size: 16px; }
        body { background: papayawhip; }
        .ONE { } .a { color: blue; }
        .TWO { } .b { color: red; }
      `)
    })

    it('should replace stylesheets on-demand', () => {
      const tagsAfterReset = Array.from(document.querySelectorAll('style'))
      expect(tagsAfterReset[0]).toBe(styleTags[0])
      expect(tagsAfterReset[1]).toBe(styleTags[1])

      /* Rerendering existing tags doesn't touch the DOM */
      const A = styled.div.withConfig({ componentId: 'ONE' })`color: blue;`
      shallow(<A />)
      const B = styled.div.withConfig({ componentId: 'TWO' })`color: red;`
      shallow(<B />)
      const styleTagsAfterRehydration = Array.from(document.querySelectorAll('style'))
      expect(styleTagsAfterRehydration[0]).toBe(styleTags[0])
      expect(styleTagsAfterRehydration[1]).toBe(styleTags[1])

      /* Only when new components are introduced (or a previous component
       * generates a new hash) does the style tag get replaced. */
      const C = styled.div.withConfig({ componentId: 'THREE' })`color: green;`
      shallow(<C />)
      const styleTagsAfterAddition = Array.from(document.querySelectorAll('style'))

      /* The first tag is unchanged */
      expect(styleTagsAfterAddition[0]).toBe(styleTags[0])
      /* The local tag has been replaced */
      expect(styleTagsAfterAddition[1]).not.toBe(styleTags[1])
      /* But it is identical, except for... */
      expect(styleTagsAfterAddition[1].outerHTML).toEqual(
        styleTags[1].outerHTML
        /* ...the new data attribute for the new classname "c"... */
          .replace(new RegExp(`${SC_ATTR}="a b"`), `${SC_ATTR}="a b c"`)
          /* ...and the new CSS before the closing tag.  */
          .replace(/(?=<\/style>)/, '/* sc-component-id: THREE */\n.THREE {}\n.c {color: green;}\n')
      )

      /* Note: any future additions don't replace the style tag */
      const D = styled.div.withConfig({ componentId: 'TWO' })`color: tomato;`
      shallow(<D />)

      expect(Array.from(document.querySelectorAll('style'))[1]).toBe(styleTagsAfterAddition[1])

      /* The point being, now we have a style tag we can inject new styles in the middle! */
      expectCSSMatches(`
        html { font-size: 16px; }
        body { background: papayawhip; }
        .ONE { } .a { color: blue; }
        .TWO { } .b { color: red; } .d { color: tomato; }
        .THREE { } .c { color: green; }
      `)
    })

    it('should leave styles if rendered in the same order they were created with', () => {
      injectGlobal`
        html { font-size: 16px; }
      `
{'fraction_non_alphanumeric': 0.17599406344691113, 'fraction_numerical': 0.01236781893513079, 'mean_word_length': 2.3468543046357615, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 103, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '618707', 'n_tokens_mistral': 5311, 'n_tokens_neox': 5096, 'n_words': 1298}