<commit_before>import faker from 'faker'
import _ from 'lodash'
import path from 'path'
import React, { createElement } from 'react'
import ReactDOMServer from 'react-dom/server'

import { createShorthand, META, numberToWord } from 'src/lib'
import { consoleUtil, sandbox, syntheticEvent } from 'test/utils'
import * as stardust from 'stardust'

import { Button, Icon, Image, Label } from 'src/elements'

const commonTestHelpers = (testName, Component) => {
  const throwError = msg => {
    throw new Error(`${testName}: ${msg} \n  Component: ${Component && Component.name}`)
  }

  const assertRequired = (required, description) => {
    return required || throwError(`Required ${description}, got: ${required} (${typeof required})`)
  }

  return {
    throwError,
    assertRequired,
  }
}

const componentCtx = require.context(
  '../../src/',
  true,
  /(addons|collections|elements|modules|views).(?!index).*\.js/
)

const componentInfo = componentCtx.keys().map(key => {
  const Component = componentCtx(key).default
  const componentType = typeof Component

  const { throwError } = commonTestHelpers('componentInfo', Component)

  if (componentType !== 'function') {
    throwError([
      `${key} is not properly exported.`,
      `Components should export a class or function, got: ${componentType}.`,
    ].join(' '))
  }

  const { _meta, prototype } = Component

  if (!_meta) {
    throwError([
      'Component is missing a static _meta object property. This should help identify it:',
      `Rendered:\n${ReactDOMServer.renderToStaticMarkup(<Component />)}`,
    ].join('\n'))
  }

  const constructorName = prototype.constructor.name
  const filePath = key
  const filename = path.basename(key)
  const filenameWithoutExt = path.basename(key, '.js')
  const subComponentName = _.has(_meta, 'parent') && _.has(_meta, 'name')
    ? _meta.name.replace(_meta.parent, '')
    : null

  // name of the component, sub component, or plural parent for sub component groups
  const componentClassName = (
    META.isChild(Component)
      ? subComponentName.replace(/Group$/, `${_meta.parent}s`)
      : _meta.name
  ).toLowerCase()

  return {
    _meta,
    Component,
    constructorName,
    componentClassName,
    subComponentName,
    filePath,
    filename,
    filenameWithoutExt,
  }
})

/**
 * Assert Component conforms to guidelines that are applicable to all components.
 * @param {React.Component|Function} Component A component that should conform.
 * @param {Object} [options={}]
 * @param {Object} [options.requiredProps={}] Props required to render Component without errors or warnings.
 */
export const isConformant = (Component, options = {}) => {
  const { requiredProps = {} } = options
  const { throwError } = commonTestHelpers('isConformant', Component)

  // tests depend on Component constructor names, enforce them
  if (!Component.prototype.constructor.name) {
    throwError([
      'Component is not a named function. This should help identify it:',
      `static _meta = ${JSON.stringify(Component._meta, null, 2)}`,
      `Rendered:\n${ReactDOMServer.renderToStaticMarkup(<Component />)}`,
    ].join('\n'))
  }

  // extract componentInfo for this component
  const {
    _meta,
    constructorName,
    componentClassName,
    filenameWithoutExt,
  } = _.find(componentInfo, i => i.constructorName === Component.prototype.constructor.name)

  // ----------------------------------------
  // Class and file name
  // ----------------------------------------
  it(`constructor name matches filename "${constructorName}"`, () => {
    constructorName.should.equal(filenameWithoutExt)
  })

  // ----------------------------------------
  // Is exported or private
  // ----------------------------------------
  // detect components like: stardust.H1
  const isStardustProp = _.has(stardust, constructorName)

  // detect sub components like: stardust.Form.Field (ie FormField component)
  // Build a path by following _meta.parents to the root:
  //   ['Form', 'FormField', 'FormTextArea']
  let stardustPath = []
  let meta = _meta
  while (meta) {
    stardustPath.unshift(meta.name)
    meta = _.get(stardust, [meta.parent, '_meta'])
  }
  // Remove parent name from paths:
  //   ['Form', 'Field', 'TextArea']
  stardustPath = stardustPath.reduce((acc, next) => (
    [...acc, next.replace(acc.join(''), '')]
  ), [])

  // find the stardustPath in the stardust object
  const isSubComponent = _.isFunction(_.get(stardust, stardustPath))

  if (META.isPrivate(constructorName)) {
    it('is not exported as a component nor sub component', () => {
      expect(isStardustProp).to.equal(
        false,
        `"${constructorName}" is private (starts with  "_").` +
        ' It cannot be a key on the stardust object'
      )

      expect(isSubComponent).to.equal(
        false,
        `"${constructorName}" is private (starts with "_").` +
        ' It cannot be a static prop of another component (sub-component)'
      )
    })
  } else {
    it('is exported as a component or sub component', () => {
      expect(isStardustProp || isSubComponent).to.equal(
        true,
        `"${constructorName}" must be:` +
        ' a key on stardust' +
        ' || key on another component (sub-component)' +
        ' || private (start with "_")'
      )<commit_msg>test(commonTests): ensure top level exports (#572)<commit_after>import faker from 'faker'
import _ from 'lodash'
import path from 'path'
import React, { createElement } from 'react'
import ReactDOMServer from 'react-dom/server'

import { createShorthand, META, numberToWord } from 'src/lib'
import { consoleUtil, sandbox, syntheticEvent } from 'test/utils'
import * as stardust from 'stardust'

import { Button, Icon, Image, Label } from 'src/elements'

const commonTestHelpers = (testName, Component) => {
  const throwError = msg => {
    throw new Error(`${testName}: ${msg} \n  Component: ${Component && Component.name}`)
  }

  const assertRequired = (required, description) => {
    return required || throwError(`Required ${description}, got: ${required} (${typeof required})`)
  }

  return {
    throwError,
    assertRequired,
  }
}

const componentCtx = require.context(
  '../../src/',
  true,
  /(addons|collections|elements|modules|views).(?!index).*\.js/
)

const componentInfo = componentCtx.keys().map(key => {
  const Component = componentCtx(key).default
  const componentType = typeof Component

  const { throwError } = commonTestHelpers('componentInfo', Component)

  if (componentType !== 'function') {
    throwError([
      `${key} is not properly exported.`,
      `Components should export a class or function, got: ${componentType}.`,
    ].join(' '))
  }

  const { _meta, prototype } = Component

  if (!_meta) {
    throwError([
      'Component is missing a static _meta object property. This should help identify it:',
      `Rendered:\n${ReactDOMServer.renderToStaticMarkup(<Component />)}`,
    ].join('\n'))
  }

  const constructorName = prototype.constructor.name
  const filePath = key
  const filename = path.basename(key)
  const filenameWithoutExt = path.basename(key, '.js')
  const subComponentName = _.has(_meta, 'parent') && _.has(_meta, 'name')
    ? _meta.name.replace(_meta.parent, '')
    : null

  // name of the component, sub component, or plural parent for sub component groups
  const componentClassName = (
    META.isChild(Component)
      ? subComponentName.replace(/Group$/, `${_meta.parent}s`)
      : _meta.name
  ).toLowerCase()

  return {
    _meta,
    Component,
    constructorName,
    componentClassName,
    subComponentName,
    filePath,
    filename,
    filenameWithoutExt,
  }
})

/**
 * Assert Component conforms to guidelines that are applicable to all components.
 * @param {React.Component|Function} Component A component that should conform.
 * @param {Object} [options={}]
 * @param {Object} [options.requiredProps={}] Props required to render Component without errors or warnings.
 */
export const isConformant = (Component, options = {}) => {
  const { requiredProps = {} } = options
  const { throwError } = commonTestHelpers('isConformant', Component)

  // tests depend on Component constructor names, enforce them
  if (!Component.prototype.constructor.name) {
    throwError([
      'Component is not a named function. This should help identify it:',
      `static _meta = ${JSON.stringify(Component._meta, null, 2)}`,
      `Rendered:\n${ReactDOMServer.renderToStaticMarkup(<Component />)}`,
    ].join('\n'))
  }

  // extract componentInfo for this component
  const {
    _meta,
    constructorName,
    componentClassName,
    filenameWithoutExt,
  } = _.find(componentInfo, i => i.constructorName === Component.prototype.constructor.name)

  // ----------------------------------------
  // Class and file name
  // ----------------------------------------
  it(`constructor name matches filename "${constructorName}"`, () => {
    constructorName.should.equal(filenameWithoutExt)
  })

  // ----------------------------------------
  // Is exported or private
  // ----------------------------------------
  // detect components like: stardust.H1
  const isStardustProp = _.has(stardust, constructorName)

  // detect sub components like: stardust.Form.Field (ie FormField component)
  // Build a path by following _meta.parents to the root:
  //   ['Form', 'FormField', 'FormTextArea']
  let stardustPath = []
  let meta = _meta
  while (meta) {
    stardustPath.unshift(meta.name)
    meta = _.get(stardust, [meta.parent, '_meta'])
  }
  // Remove parent name from paths:
  //   ['Form', 'Field', 'TextArea']
  stardustPath = stardustPath.reduce((acc, next) => (
    [...acc, next.replace(acc.join(''), '')]
  ), [])

  // find the stardustPath in the stardust object
  const isSubComponent = _.isFunction(_.get(stardust, stardustPath))

  if (META.isPrivate(constructorName)) {
    it('is not exported as a component nor sub component', () => {
      expect(isStardustProp).to.equal(
        false,
        `"${constructorName}" is private (starts with  "_").` +
        ' It cannot be a key on the stardust object'
      )

      expect(isSubComponent).to.equal(
        false,
        `"${constructorName}" is private (starts with "_").` +
        ' It cannot be a static prop of another component (sub-component)'
      )
    })
  } else {
    // require all components to be exported at the top level
    it('is exported at the top level', () => {
      expect(isStardustProp).to.equal(true, [
        `"${constructorName}" must be exported at top level.`,
        `According to its \`_meta.type\`, export it in \`${_meta.type}s/index.js\`.`,
      ].join(' '))
{'fraction_non_alphanumeric': 0.16279505432746347, 'fraction_numerical': 0.0006556762832521544, 'mean_word_length': 3.0063789868667916, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 27, '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': '5493330', 'n_tokens_mistral': 3079, 'n_tokens_neox': 3050, 'n_words': 862}