<commit_before>/* global alert */
var $ = require('jquery')

var yo = require('yo-yo')
const copy = require('clipboard-copy')

var parseContracts = require('../contract/contractParser')
var publishOnSwarm = require('../contract/publishOnSwarm')
var modalDialog = require('../ui/modaldialog')

// -------------- styling ----------------------
var csjs = require('csjs-inject')
var remix = require('ethereum-remix')
var styleGuide = remix.ui.styleGuide
var styles = styleGuide()

var css = csjs`
  .compileTabView {
    padding: 2%;
  }
  .contract {
    display: block;
    margin: 3% 0;
  }
  .compileContainer  {
    ${styles.displayBox}
    margin-bottom: 2%;
  }
  .autocompileContainer {
    width: 90px;
    display: flex;
    align-items: center;
  }
  .autocompile {}
  .autocompileTitle {
    font-weight: bold;
    margin: 1% 0;
  }
  .autocompileText {
    margin: 1% 0;
    font-size: 12px;
    overflow: hidden;
    word-break: normal;
    line-height: initial;
  }
  .warnCompilationSlow {
    color: ${styles.colors.orange};
    margin-left: 1%;
  }
  .compileButtons {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
  }
  .name {
    display: flex;
  }
  .size {
    display: flex;
  }
  .compileButton {
    ${styles.button}
    width: 120px;
    min-width: 110px;
    margin-right: 1%;
    font-size: 12px;
  }
  .container {
    ${styles.displayBox}
    margin: 0;
    display: flex;
    align-items: center;
  }
  .contractNames {
    ${styles.dropdown}
    margin-right: 5%;
  }
  .contractButtons {
    display: flex;
    cursor: pointer;
    justify-content: center;
    text-align: center;
  }
  .details {
    ${styles.button}
  }
  .publish {
    ${styles.button}
    margin-left: 2%;
  }
  .log {
    display: flex;
    flex-direction: column;
    align-items: baseline
  }
  .key {
    margin-right: 5px;
    color: grey;
    text-transform: uppercase;
    width: 100%;
  }
  .value {
    display: flex;
    width: 100%;
    margin-top: 1.5%;
  }
  .copyDetails {
    margin-left: 2%;
    font-size: 14px;
    cursor: pointer;
    color: ${styles.colors.grey};
    opacity: .3;
  }
  .copyDetails:hover {
    opacity: 1;
  }
  .detailsJSON {
    padding: 8px 0;
    background-color: ${styles.colors.white};
    border: none;
    color: ${styles.colors.grey};
  }
  .icon {
    margin-right: 3%;
  }
  .spinningIcon {
    margin-right: .3em;
    animation: spin 2s linear infinite;
  }
  .bouncingIcon {
    margin-right: .3em;
    animation: bounce 2s infinite;
  }
  @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
  }
  @-webkit-keyframes bounce {
  0% {
    margin-bottom: 0;
    color: ${styles.colors.transparent};
  }
  70% {
    margin-bottom: 0;
    color: ${styles.colors.grey};
  }
  100% {
    margin-bottom: 0;
    color: ${styles.colors.transparent};
  }
}
`

module.exports = compileTab

function compileTab (container, appAPI, appEvents, opts) {
  if (typeof container === 'string') container = document.querySelector(container)
  if (!container) throw new Error('no container given')

  // Containers
  var warnCompilationSlow = yo`<i title="Copy Address" style="display:none" class="${css.warnCompilationSlow} fa fa-exclamation-triangle" aria-hidden="true"></i>`
  var compileIcon = yo`<i class="fa fa-refresh ${css.icon}" aria-hidden="true"></i>`
  var compileContainer = yo`

${compileIcon} Start to compile

            <input class="${css.autocompile}" id="autoCompile" type="checkbox" checked title="Auto compile">
            Auto compile

          ${warnCompilationSlow}

  `

  compileContainer.querySelector('#compile').addEventListener('click', () => {
    appAPI.runCompiler()
  })

  var compileTimeout = null
  function scheduleCompilation () {
    if (!appAPI.config.get('autoCompile')) {
      return
    }

    if (compileTimeout) {
      window.clearTimeout(compileTimeout)
    }
    compileTimeout = window.setTimeout(() => {
      appAPI.runCompiler()
    }, 300)
  }

  appEvents.editor.register('contentChanged', () => {
    scheduleCompilation()
  })

  appEvents.editor.register('sessionSwitched', () => {
    scheduleCompilation()
  })

  // ----------------- autoCompile -----------------
  var autoCompileInput = compileContainer.querySelector('#autoCompile')
  var autoCompile = true
  if (appAPI.config.exists('autoCompile')) {
    autoCompile = appAPI.config.get('autoCompile')
  }
  appAPI.config.set('autoCompile', autoCompile)
  autoCompileInput.setAttribute('checked', autoCompile)

  autoCompileInput.addEventListener('change', function () {
    appAPI.config.set('autoCompile', autoCompileInput.checked)
  })

  // REGISTER EVENTS

  // compilationDuration
  appEvents.compiler.register('compilationDuration', function tabHighlighting (speed) {
    if (speed > 1000) {
      warnCompilationSlow.setAttribute('title', `Last compilation took ${speed}ms. We suggest to turn off autocompilation.`)
      warnCompilationSlow.style.display = 'inline-block'
    } else {
      warnCompilationSlow.style.display = 'none'
    }
  })
  // loadingCompiler
  appEvents.editor.register('contentChanged', function changedFile () {
    var compileTab = document.querySelector('.compileView')
    compileTab.style.color = styles.colors.red
    compileIcon.classList.add(`${css.bouncingIcon}`)
  })
  appEvents.compiler.register('loadingCompiler', function start () {
    compileIcon.classList.add(`${css.spinningIcon}`)
    warnCompilationSlow.style.display = 'none'
    compileIcon.setAttribute('title', 'compiler is loading, please wait a few moments.')
  })
  appEvents.compiler.register('compilationFinished', function finish () {
    var compileTab = document.querySelector('.compileView')
    compileTab.style.color = styles.colors.black
    compileIcon.style.color = styles.colors.black
    compileIcon.classList.remove(`${css.spinningIcon}`)
    compileIcon.classList.remove(`${css.bouncingIcon}`)
    compileIcon.setAttribute('title', 'idle')
  })
  appEvents.compiler.register('compilationStarted', function start () {
    compileIcon.classList.remove(`${css.bouncingIcon}`)
    compileIcon.classList.add(`${css.spinningIcon}`)
    compileIcon.setAttribute('title', 'compiling...')
  })
  appEvents.compiler.register('compilerLoaded', function loaded () {
    compileIcon.classList.remove(`${css.spinningIcon}`)
    compileIcon.setAttribute('title', '')
  })

  var el = yo`

      ${compileContainer}
      ${contractNames(container, appAPI, appEvents, opts)}

  `
  container.appendChild(el)

  /* ------------------------------------------------
    section CONTRACT DROPDOWN, DETAILS AND PUBLISH
  ------------------------------------------------ */

  function contractNames (container, appAPI, appEvents, opts) {
    var contractsDetails = {}
    appEvents.compiler.register('compilationFinished', function (success, data, source) {
      // reset the contractMetadata list (used by the publish action)
      contractsDetails = {}
      // refill the dropdown list
      getContractNames(success, data)
      // hightlight the tab if error
      if (success) {
        document.querySelector('#righthand-panel #menu .compileView').style.color = ''
      } else {
        document.querySelector('#righthand-panel #menu .compileView').style.color = styles.colors.red
      }
      // display warning error if any
      var errorContainer = container.querySelector('.error')
      errorContainer.innerHTML = ''
      if (data['error']) {
        appAPI.compilationMessage(data['error'], $(errorContainer))
      }
      if (data['errors']) {
        data['errors'].forEach(function (err) {
          appAPI.compilationMessage(err, $(errorContainer))
        })
      }
      if (errorContainer.innerHTML === '') {
        appAPI.compilationMessage('Compilation successful without warning', $(errorContainer), {type: 'success'})
      }
    })

    var el = yo`

        <select class="${css.contractNames}" disabled></select>

 { details() }}>Details

 { publish(appAPI) }}>Publish

    `

    // HELPERS

    // GET NAMES OF ALL THE CONTRACTS
    function getContractNames (success, data) {
      var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
      contractNames.innerHTML = ''
      if (success) {
        contractNames.removeAttribute('disabled')
        for (var name in data.contracts) {
          contractsDetails[name] = parseContracts(name, data.contracts[name], appAPI.currentCompiledSourceCode())
          var contractName = yo`
            <option>

${name}

            </option>`
          contractNames.appendChild(contractName)
        }
        appAPI.resetDapp(contractsDetails)
      } else {
        contractNames.setAttribute('disabled', true)
        appAPI.resetDapp({})
      }
    }

    function details () {
      var select = el.querySelector('select')
      if (select.children.length > 0 && select.selectedIndex >= 0) {
        var contractName = select.children[select.selectedIndex].innerText
        var details = contractsDetails[contractName]
        var keys = Object.keys(contractsDetails[contractName])
        var log = yo`<div class="${css.detailsJSON}"></div>`
        keys.map(x => {
          var copyDetails = yo`<span class="${css.copyDetails}"><i title="Copy value to clipboard" class="fa fa-clipboard" onclick=${() => { copy(details[x]) }} aria-hidden="true"></i></span>`
          log.appendChild(yo`

${x} ${copyDetails}

              ${insertValue(details, x)}

            `)
        })
        modalDialog(contractName, log, {label: 'OK'}, {label: ''})
      }
    }

    function insertValue (details, x) {
      var value = yo`<pre class="${css.value}"></pre>`
      var node
      if (x === 'bytecode' || x === 'metadataHash' || x === 'swarmLocation' || x === 'Runtime Bytecode' || x === 'Opcodes') {
        node = yo`<div>${JSON.stringify((details[x].slice(1, 40) + '...'), null, 4)}</div>`
      } else {
        node = yo`<div>${JSON.stringify(details[x], null, 4)}</div>`
      }
      value.appendChild(node)
      return value
    }

    function publish (appAPI) {
      var selectContractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
      if (selectContractNames.children.length > 0 && selectContractNames.selectedIndex >= 0) {
        var contract = contractsDetails[selectContractNames.children[selectContractNames.selectedIndex].innerText]
        publishOnSwarm(contract, appAPI, function (err) {
          if (err) {
            alert('Failed to publish metadata: ' + err)
          } else {
            alert('Metadata published successfully')
          }
        })
      }
    }
    return el
  }
}
<commit_msg>manage webDeploy as pre, interface and metadata as TreeView<commit_after>/* global alert */
var $ = require('jquery')

var yo = require('yo-yo')
const copy = require('clipboard-copy')

var parseContracts = require('../contract/contractParser')
var publishOnSwarm = require('../contract/publishOnSwarm')
var modalDialog = require('../ui/modaldialog')
var TreeView = require('ethereum-remix').ui.TreeView

// -------------- styling ----------------------
var csjs = require('csjs-inject')
var remix = require('ethereum-remix')
var styleGuide = remix.ui.styleGuide
var styles = styleGuide()

var css = csjs`
  .compileTabView {
    padding: 2%;
  }
  .contract {
    display: block;
    margin: 3% 0;
  }
  .compileContainer  {
    ${styles.displayBox}
    margin-bottom: 2%;
  }
  .autocompileContainer {
    width: 90px;
    display: flex;
    align-items: center;
  }
  .autocompile {}
  .autocompileTitle {
    font-weight: bold;
    margin: 1% 0;
  }
  .autocompileText {
    margin: 1% 0;
    font-size: 12px;
    overflow: hidden;
    word-break: normal;
    line-height: initial;
  }
  .warnCompilationSlow {
    color: ${styles.colors.orange};
    margin-left: 1%;
  }
  .compileButtons {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
  }
  .name {
    display: flex;
  }
  .size {
    display: flex;
  }
  .compileButton {
    ${styles.button}
    width: 120px;
    min-width: 110px;
    margin-right: 1%;
    font-size: 12px;
  }
  .container {
    ${styles.displayBox}
    margin: 0;
    display: flex;
    align-items: center;
  }
  .contractNames {
    ${styles.dropdown}
    margin-right: 5%;
  }
  .contractButtons {
    display: flex;
    cursor: pointer;
    justify-content: center;
    text-align: center;
  }
  .details {
    ${styles.button}
  }
  .publish {
    ${styles.button}
    margin-left: 2%;
  }
  .log {
    display: flex;
    flex-direction: column;
    align-items: baseline
  }
  .key {
    margin-right: 5px;
    color: grey;
    text-transform: uppercase;
    width: 100%;
  }
  .value {
    display: flex;
    width: 100%;
    margin-top: 1.5%;
  }
  .copyDetails {
    margin-left: 2%;
    font-size: 14px;
    cursor: pointer;
    color: ${styles.colors.grey};
    opacity: .3;
  }
  .copyDetails:hover {
    opacity: 1;
  }
  .detailsJSON {
    padding: 8px 0;
    background-color: ${styles.colors.white};
    border: none;
    color: ${styles.colors.grey};
  }
  .icon {
    margin-right: 3%;
  }
  .spinningIcon {
    margin-right: .3em;
    animation: spin 2s linear infinite;
  }
  .bouncingIcon {
    margin-right: .3em;
    animation: bounce 2s infinite;
  }
  @keyframes spin {
      0% { transform: rotate(0deg); }
      100% { transform: rotate(360deg); }
  }
  @-webkit-keyframes bounce {
  0% {
    margin-bottom: 0;
    color: ${styles.colors.transparent};
  }
  70% {
    margin-bottom: 0;
    color: ${styles.colors.grey};
  }
  100% {
    margin-bottom: 0;
    color: ${styles.colors.transparent};
  }
}
`

module.exports = compileTab

function compileTab (container, appAPI, appEvents, opts) {
  if (typeof container === 'string') container = document.querySelector(container)
  if (!container) throw new Error('no container given')

  // Containers
  var warnCompilationSlow = yo`<i title="Copy Address" style="display:none" class="${css.warnCompilationSlow} fa fa-exclamation-triangle" aria-hidden="true"></i>`
  var compileIcon = yo`<i class="fa fa-refresh ${css.icon}" aria-hidden="true"></i>`
  var compileContainer = yo`

${compileIcon} Start to compile

            <input class="${css.autocompile}" id="autoCompile" type="checkbox" checked title="Auto compile">
            Auto compile

          ${warnCompilationSlow}

  `

  compileContainer.querySelector('#compile').addEventListener('click', () => {
    appAPI.runCompiler()
  })

  var compileTimeout = null
  function scheduleCompilation () {
    if (!appAPI.config.get('autoCompile')) {
      return
    }

    if (compileTimeout) {
      window.clearTimeout(compileTimeout)
    }
    compileTimeout = window.setTimeout(() => {
      appAPI.runCompiler()
    }, 300)
  }

  appEvents.editor.register('contentChanged', () => {
    scheduleCompilation()
  })

  appEvents.editor.register('sessionSwitched', () => {
    scheduleCompilation()
  })

  // ----------------- autoCompile -----------------
  var autoCompileInput = compileContainer.querySelector('#autoCompile')
  var autoCompile = true
  if (appAPI.config.exists('autoCompile')) {
    autoCompile = appAPI.config.get('autoCompile')
  }
  appAPI.config.set('autoCompile', autoCompile)
  autoCompileInput.setAttribute('checked', autoCompile)

  autoCompileInput.addEventListener('change', function () {
    appAPI.config.set('autoCompile', autoCompileInput.checked)
  })

  // REGISTER EVENTS

  // compilationDuration
  appEvents.compiler.register('compilationDuration', function tabHighlighting (speed) {
    if (speed > 1000) {
      warnCompilationSlow.setAttribute('title', `Last compilation took ${speed}ms. We suggest to turn off autocompilation.`)
      warnCompilationSlow.style.display = 'inline-block'
    } else {
      warnCompilationSlow.style.display = 'none'
    }
  })
  // loadingCompiler
  appEvents.editor.register('contentChanged', function changedFile () {
    var compileTab = document.querySelector('.compileView')
    compileTab.style.color = styles.colors.red
    compileIcon.classList.add(`${css.bouncingIcon}`)
  })
  appEvents.compiler.register('loadingCompiler', function start () {
    compileIcon.classList.add(`${css.spinningIcon}`)
    warnCompilationSlow.style.display = 'none'
    compileIcon.setAttribute('title', 'compiler is loading, please wait a few moments.')
  })
  appEvents.compiler.register('compilationFinished', function finish () {
    var compileTab = document.querySelector('.compileView')
    compileTab.style.color = styles.colors.black
    compileIcon.style.color = styles.colors.black
    compileIcon.classList.remove(`${css.spinningIcon}`)
    compileIcon.classList.remove(`${css.bouncingIcon}`)
    compileIcon.setAttribute('title', 'idle')
  })
  appEvents.compiler.register('compilationStarted', function start () {
    compileIcon.classList.remove(`${css.bouncingIcon}`)
    compileIcon.classList.add(`${css.spinningIcon}`)
    compileIcon.setAttribute('title', 'compiling...')
  })
  appEvents.compiler.register('compilerLoaded', function loaded () {
    compileIcon.classList.remove(`${css.spinningIcon}`)
    compileIcon.setAttribute('title', '')
  })

  var el = yo`

      ${compileContainer}
      ${contractNames(container, appAPI, appEvents, opts)}

  `
  container.appendChild(el)

  /* ------------------------------------------------
    section CONTRACT DROPDOWN, DETAILS AND PUBLISH
  ------------------------------------------------ */

  function contractNames (container, appAPI, appEvents, opts) {
    var contractsDetails = {}
    appEvents.compiler.register('compilationFinished', function (success, data, source) {
      // reset the contractMetadata list (used by the publish action)
      contractsDetails = {}
      // refill the dropdown list
      getContractNames(success, data)
      // hightlight the tab if error
      if (success) {
        document.querySelector('#righthand-panel #menu .compileView').style.color = ''
      } else {
        document.querySelector('#righthand-panel #menu .compileView').style.color = styles.colors.red
      }
      // display warning error if any
      var errorContainer = container.querySelector('.error')
      errorContainer.innerHTML = ''
      if (data['error']) {
        appAPI.compilationMessage(data['error'], $(errorContainer))
      }
      if (data['errors']) {
        data['errors'].forEach(function (err) {
          appAPI.compilationMessage(err, $(errorContainer))
        })
      }
      if (errorContainer.innerHTML === '') {
        appAPI.compilationMessage('Compilation successful without warning', $(errorContainer), {type: 'success'})
      }
    })

    var el = yo`

        <select class="${css.contractNames}" disabled></select>

 { details() }}>Details

 { publish(appAPI) }}>Publish

    `

    // HELPERS

    // GET NAMES OF ALL THE CONTRACTS
    function getContractNames (success, data) {
      var contractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
      contractNames.innerHTML = ''
      if (success) {
        contractNames.removeAttribute('disabled')
        for (var name in data.contracts) {
          contractsDetails[name] = parseContracts(name, data.contracts[name], appAPI.currentCompiledSourceCode())
          var contractName = yo`
            <option>

${name}

            </option>`
          contractNames.appendChild(contractName)
        }
        appAPI.resetDapp(contractsDetails)
      } else {
        contractNames.setAttribute('disabled', true)
        appAPI.resetDapp({})
      }
    }

    function details () {
      var select = el.querySelector('select')
      if (select.children.length > 0 && select.selectedIndex >= 0) {
        var contractName = select.children[select.selectedIndex].innerText
        var details = contractsDetails[contractName]
        var keys = Object.keys(contractsDetails[contractName])
        var log = yo`<div class="${css.detailsJSON}"></div>`
        keys.map(x => {
          var copyDetails = yo`<span class="${css.copyDetails}"><i title="Copy value to clipboard" class="fa fa-clipboard" onclick=${() => { copy(details[x]) }} aria-hidden="true"></i></span>`
          log.appendChild(yo`

${x} ${copyDetails}

              ${insertValue(details, x)}

            `)
        })
        modalDialog(contractName, log, {label: 'OK'}, {label: ''})
      }
    }

    function insertValue (details, x) {
      var value = yo`<pre class="${css.value}"></pre>`
      var node
      if (x === 'bytecode' || x === 'metadataHash' || x === 'swarmLocation' || x === 'Runtime Bytecode' || x === 'Opcodes') {
        node = yo`<div>${details[x]}</div>`
      } else if (x === 'web3Deploy') {
        node = yo`<pre>${details[x]}</pre>`
      } else if (x === 'interface' || x === 'metadata') {
        var treeView = new TreeView({})
        node = yo`<div>${treeView.render(JSON.parse(details[x]))}</div>`
      } else {
        node = yo`<div>${JSON.stringify(details[x], null, 4)}</div>`
      }
      value.appendChild(node)
      return value
    }

    function publish (appAPI) {
      var selectContractNames = document.querySelector(`.${css.contractNames.classNames[0]}`)
      if (selectContractNames.children.length > 0 && selectContractNames.selectedIndex >= 0) {
        var contract = contractsDetails[selectContractNames.children[selectContractNames.selectedIndex].innerText]
        publishOnSwarm(contract, appAPI, function (err) {
          if (err) {
            alert('Failed to publish metadata: ' + err)
          } else {
            alert('Metadata published successfully')
          }
        })
      }
    }
    return el
  }
}
<|endoftext|>


<commit_before>(function() {
  var parser = window.BetterMarkdown,
      MD = parser.Markdown,
      DialectHelpers = parser.DialectHelpers;

  function generateUniqueishIdentifier () {
    // Stolen from StackOverflow: http://stackoverflow.com/a/8809472
    var d = new Date().getTime();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (d + Math.random()*16)%16 | 0;
        d = Math.floor(d/16);
        return (c=='x' ? r : (r&0x3|0x8)).toString(16);
    });
    return uuid;
  }

  function generateJsonML (label, contents, opts) {
    var myId = 'spoilerrific-' + generateUniqueishIdentifier();

    var loginButton = '<a href="<%= path "/login"%>" class="btn btn-primary btn-small login-button"> <%= I18n.t("log_in") %>';

    var wrappingDiv = ['div class="show-logged-in"'].concat(contents);

    return ['div',
      { 'class': 'spoilerrific', 'data-prefix': opts.usingDefaultLabel ? 'yes' : 'no', 'data-suffix': opts.usingDefaultLabel ? 'no' : 'yes' },
      ['input',
        { 'type': 'checkbox', 'id': myId }
      ],
      ['label',
        { 'for': myId }, label
      ],
      wrappingDiv,
      ['div',
        { 'class': 'hide-logged-out'}, 'You must have an account to view this.
',
        ['button',
          { 'onclick': 'Discourse.__container__.lookup("router:main").send("showCreateAccount");', 'class': 'btn btn-primary login-button'},
          ['i',
          { 'class': 'fa'} ]
          , 'Sign Up'

        ],
        ['button',
          { 'onclick': 'Discourse.__container__.lookup("router:main").send("showLogin");', 'class': 'btn btn-primary login-button'},
          ['i',
          { 'class': 'fa fa-user'} ]
          , ' Log In'
        ]
      ]
    ];
  }

  // At the moment, Discourse's blocking mechanism seems to be broken
  // such that the below code will only accept block-level Markdown tags
  // (e.g. headers) if they appear as the very first item in the block
  // Therefore, as a (rather hackish) workaround, we recursively invoke
  // the ENTIRE markdown processing chain...
  //var emitter = function(contents, params) {
  //  params = params || '';
  //  var label = params.replace(/(^")|("$)/g, '') || 'FLARD inside!';
  //  return generateJsonML(label, contents);
  //};
  //Discourse.BBCode.register('spoiler', {noWrap: true}, emitter);

  Discourse.Dialect.replaceBlock({
    start: /\[spoiler(=[^\[\]]+)?\]([\s\S]*)/igm,
    stop: /\[\/spoiler\]/igm,
    rawContents: true, // this is documented, but doesn't seem to do anything
    emitter: function(blockContents, matches) {
      var params = matches[1] ? matches[1].replace(/^=/g, '') : '',
          label = params.replace(/(^")|("$)/g, ''),
          opts = {
            usingDefaultLabel: false,
          },
          inner = blockContents.join("\n\n"),
          innerTree = parser.toHTMLTree(inner, "Discourse");

      if (!label) {
          opts.usingDefaultLabel = true;
          label = Discourse.SiteSettings.spoiler_default_label;
      }

      if (!innerTree || innerTree.length === 0 || innerTree[0] != 'html') { // uh?
          return generateJsonML(label, inner, opts);
      }

      return generateJsonML(label, innerTree.slice(1), opts);
    }
  });

  var spoilerrificRe = /^spoilerrific-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/;
  Discourse.Markdown.whiteListTag('button', 'class', 'btn btn-primary login-button');
  Discourse.Markdown.whiteListTag('button', 'onclick', 'Discourse.__container__.lookup("router:main").send("showCreateAccount");');
  Discourse.Markdown.whiteListTag('button', 'onclick', 'Discourse.__container__.lookup("router:main").send("showLogin");');
  Discourse.Markdown.whiteListTag('i', 'class', 'fa fa-user');
  Discourse.Markdown.whiteListTag('div', 'class', 'spoilerrific');
  Discourse.Markdown.whiteListTag('div', 'class', 'show-logged-in');
  Discourse.Markdown.whiteListTag('div', 'class', 'hide-logged-out');
  Discourse.Markdown.whiteListTag('input', 'type', 'checkbox');
  Discourse.Markdown.whiteListTag('input', 'checked', 'checked');
  Discourse.Markdown.whiteListTag('input', 'id', spoilerrificRe);
  Discourse.Markdown.whiteListTag('label', 'for', spoilerrificRe);
  Discourse.Markdown.whiteListTag('label', 'data-prefix', /^(yes|no)$/);
  Discourse.Markdown.whiteListTag('label', 'data-suffix', /^(yes|no)$/);
})();
<commit_msg>Changing text of sign up page<commit_after>(function() {
  var parser = window.BetterMarkdown,
      MD = parser.Markdown,
      DialectHelpers = parser.DialectHelpers;

  function generateUniqueishIdentifier () {
    // Stolen from StackOverflow: http://stackoverflow.com/a/8809472
    var d = new Date().getTime();
    var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = (d + Math.random()*16)%16 | 0;
        d = Math.floor(d/16);
        return (c=='x' ? r : (r&0x3|0x8)).toString(16);
    });
    return uuid;
  }

  function generateJsonML (label, contents, opts) {
    var myId = 'spoilerrific-' + generateUniqueishIdentifier();

    var loginButton = '<a href="<%= path "/login"%>" class="btn btn-primary btn-small login-button"> <%= I18n.t("log_in") %>';

    var wrappingDiv = ['div class="show-logged-in"'].concat(contents);

    return ['div',
      { 'class': 'spoilerrific', 'data-prefix': opts.usingDefaultLabel ? 'yes' : 'no', 'data-suffix': opts.usingDefaultLabel ? 'no' : 'yes' },
      ['input',
        { 'type': 'checkbox', 'id': myId }
      ],
      ['label',
        { 'for': myId }, label
      ],
      wrappingDiv,
      ['div',
        { 'class': 'hide-logged-out'}, 'Log in to learn more
',
        ['button',
          { 'onclick': 'Discourse.__container__.lookup("router:main").send("showCreateAccount");', 'class': 'btn btn-primary login-button'},
          ['i',
          { 'class': 'fa'} ]
          , 'Sign Up'

        ],
        ['button',
          { 'onclick': 'Discourse.__container__.lookup("router:main").send("showLogin");', 'class': 'btn btn-primary login-button'},
          ['i',
          { 'class': 'fa fa-user'} ]
          , ' Log In'
        ]
      ]
    ];
  }

  // At the moment, Discourse's blocking mechanism seems to be broken
  // such that the below code will only accept block-level Markdown tags
  // (e.g. headers) if they appear as the very first item in the block
  // Therefore, as a (rather hackish) workaround, we recursively invoke
  // the ENTIRE markdown processing chain...
  //var emitter = function(contents, params) {
  //  params = params || '';
  //  var label = params.replace(/(^")|("$)/g, '') || 'FLARD inside!';
  //  return generateJsonML(label, contents);
  //};
  //Discourse.BBCode.register('spoiler', {noWrap: true}, emitter);

  Discourse.Dialect.replaceBlock({
    start: /\[spoiler(=[^\[\]]+)?\]([\s\S]*)/igm,
    stop: /\[\/spoiler\]/igm,
    rawContents: true, // this is documented, but doesn't seem to do anything
    emitter: function(blockContents, matches) {
      var params = matches[1] ? matches[1].replace(/^=/g, '') : '',
          label = params.replace(/(^")|("$)/g, ''),
          opts = {
            usingDefaultLabel: false,
          },
          inner = blockContents.join("\n\n"),
          innerTree = parser.toHTMLTree(inner, "Discourse");

      if (!label) {
          opts.usingDefaultLabel = true;
          label = Discourse.SiteSettings.spoiler_default_label;
      }

      if (!innerTree || innerTree.length === 0 || innerTree[0] != 'html') { // uh?
          return generateJsonML(label, inner, opts);
      }

      return generateJsonML(label, innerTree.slice(1), opts);
    }
  });

  var spoilerrificRe = /^spoilerrific-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$/;
  Discourse.Markdown.whiteListTag('button', 'class', 'btn btn-primary login-button');
  Discourse.Markdown.whiteListTag('button', 'onclick', 'Discourse.__container__.lookup("router:main").send("showCreateAccount");');
  Discourse.Markdown.whiteListTag('button', 'onclick', 'Discourse.__container__.lookup("router:main").send("showLogin");');
  Discourse.Markdown.whiteListTag('i', 'class', 'fa fa-user');
  Discourse.Markdown.whiteListTag('div', 'class', 'spoilerrific');
  Discourse.Markdown.whiteListTag('div', 'class', 'show-logged-in');
  Discourse.Markdown.whiteListTag('div', 'class', 'hide-logged-out');
  Discourse.Markdown.whiteListTag('input', 'type', 'checkbox');
  Discourse.Markdown.whiteListTag('input', 'checked', 'checked');
  Discourse.Markdown.whiteListTag('input', 'id', spoilerrificRe);
  Discourse.Markdown.whiteListTag('label', 'for', spoilerrificRe);
  Discourse.Markdown.whiteListTag('label', 'data-prefix', /^(yes|no)$/);
  Discourse.Markdown.whiteListTag('label', 'data-suffix', /^(yes|no)$/);
})();
<|endoftext|>


<commit_before>const webpack = require('webpack');
const baseConfig = require('./webpack.config.base');

const config = Object.assign(
  {
    devtool: 'source-map',

    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify('production'),
      }),
    ],
  },
  baseConfig,
);

const appConfig = {
  target: 'electron-renderer',

  entry: {
    app: ['./src/app'],
  },

  externals: {
    sqlite3: 'commonjs sqlite3',
    npm: 'require("npm")',
  },
};

const newTabConfig = {
  target: 'web',

  entry: {
    newtab: ['react-hot-loader/patch', './src/newtab'],
  },
};

const testFieldConfig = {
  target: 'web',

  entry: {
    testField: ['react-hot-loader/patch', './src/test-field'],
  },
};

function getConfig(cfg) {
  return Object.assign({}, config, cfg);
}

module.exports = [getConfig(appConfig), getConfig(newTabConfig), getConfig(testFieldConfig)];
<commit_msg>:wrench: Fix production build<commit_after>const webpack = require('webpack');
const baseConfig = require('./webpack.config.base');

const config = Object.assign(
  {
    devtool: 'source-map',

    plugins: [
      new webpack.DefinePlugin({
        'process.env.NODE_ENV': JSON.stringify('production'),
      }),
    ],
  },
  baseConfig,
);

const appConfig = {
  target: 'electron-renderer',

  entry: {
    app: ['./src/app'],
  },

  externals: {
    sqlite3: 'commonjs sqlite3',
    npm: 'require("npm")',
  },
};

const newTabConfig = {
  target: 'web',

  entry: {
    newtab: ['./src/newtab'],
  },
};

const testFieldConfig = {
  target: 'web',

  entry: {
    testField: ['./src/test-field'],
  },
};

function getConfig(cfg) {
  return Object.assign({}, config, cfg);
}

module.exports = [getConfig(appConfig), getConfig(newTabConfig), getConfig(testFieldConfig)];
<|endoftext|>


<commit_before>import { waitFor } from '@testing-library/react';

import { setViewContext } from 'amo/actions/viewContext';
import { EXPERIMENT_CONFIG } from 'amo/experiments/20220908_amo_dimension_test';
import { GET_LANDING, getLanding, loadLanding } from 'amo/reducers/landing';
import {
  ADDON_TYPE_EXTENSION,
  ADDON_TYPE_STATIC_THEME,
  CLIENT_APP_FIREFOX,
  LANDING_PAGE_EXTENSION_COUNT,
  LANDING_PAGE_THEME_COUNT,
  RECOMMENDED,
  SEARCH_SORT_RANDOM,
  SEARCH_SORT_TRENDING,
  SEARCH_SORT_TOP_RATED,
} from 'amo/constants';
import tracking from 'amo/tracking';
import { getCanonicalURL, visibleAddonType } from 'amo/utils';
import {
  EXPERIMENT_ID_GA_DIMENSION,
  EXPERIMENT_VARIATION_GA_DIMENSION,
} from 'amo/withExperiment';
import {
  changeLocation,
  createAddonsApiResult,
  createFailedErrorHandler,
  createLocalizedString,
  dispatchClientMetadata,
  fakeAddon,
  getElement,
  renderPage as defaultRender,
  screen,
} from 'tests/unit/helpers';

jest.mock('amo/tracking', () => ({
  ...jest.requireActual('amo/tracking'),
  sendEvent: jest.fn(),
  setDimension: jest.fn(),
}));

describe(__filename, () => {
  const clientApp = CLIENT_APP_FIREFOX;
  const lang = 'en-US';
  const errorHandlerId = 'LandingPage';
  const getLocation = (addonType = ADDON_TYPE_EXTENSION) =>
    `/${lang}/${clientApp}/${visibleAddonType(addonType)}/`;
  let history;
  let store;

  beforeEach(() => {
    store = dispatchClientMetadata({ clientApp, lang }).store;
  });

  afterEach(() => {
    jest.clearAllMocks().resetModules();
  });

  const render = ({ addonType = ADDON_TYPE_EXTENSION, location } = {}) => {
    const renderResults = defaultRender({
      initialEntries: [location || getLocation(addonType)],
      store,
    });
    history = renderResults.history;
    return renderResults;
  };

  const _getAndLoadLandingAddons = ({
    addonType = ADDON_TYPE_EXTENSION,
    numberOfAddons = 1,
    ...otherParams
  } = {}) => {
    store.dispatch(
      getLanding({
        addonType,
        errorHandlerId,
      }),
    );
    store.dispatch(
      loadLanding({
        addonType,
        recommended: createAddonsApiResult(
          Array(numberOfAddons).fill({
            ...fakeAddon,
            name: createLocalizedString('Featured'),
            slug: 'recommended',
            type: addonType,
          }),
        ),
        highlyRated: createAddonsApiResult(
          Array(numberOfAddons).fill({
            ...fakeAddon,
            name: createLocalizedString('High'),
            slug: 'high',
            type: addonType,
          }),
        ),
        trending: createAddonsApiResult(
          Array(numberOfAddons).fill({
            ...fakeAddon,
            name: createLocalizedString('Trending'),
            slug: 'trending',
            type: addonType,
          }),
        ),
        ...otherParams,
      }),
    );
  };

  it('dispatches setViewContext on load and update', async () => {
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).toHaveBeenCalledWith(setViewContext(ADDON_TYPE_EXTENSION));

    dispatch.mockClear();

    await changeLocation({
      history,
      pathname: getLocation(ADDON_TYPE_STATIC_THEME),
    });

    expect(dispatch).toHaveBeenCalledWith(
      setViewContext(ADDON_TYPE_STATIC_THEME),
    );
  });

  it('dispatches getLanding when results are not loaded', () => {
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).toHaveBeenCalledWith(
      getLanding({
        addonType: ADDON_TYPE_EXTENSION,
        errorHandlerId,
      }),
    );
  });

  it('dispatches getLanding when addon type changes', async () => {
    // We load theme add-ons.
    _getAndLoadLandingAddons({ addonType: ADDON_TYPE_STATIC_THEME });
    store.dispatch(setViewContext(ADDON_TYPE_STATIC_THEME));
    const dispatch = jest.spyOn(store, 'dispatch');
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    dispatch.mockClear();

    // Now we request extension add-ons.
    await changeLocation({
      history,
      pathname: getLocation(ADDON_TYPE_EXTENSION),
    });

    expect(dispatch).toHaveBeenCalledWith(
      getLanding({
        addonType: ADDON_TYPE_EXTENSION,
        errorHandlerId,
      }),
    );
    expect(dispatch).toHaveBeenCalledWith(setViewContext(ADDON_TYPE_EXTENSION));
  });

  it('does not dispatch getLanding when addon type does not change', async () => {
    const addonType = ADDON_TYPE_EXTENSION;

    // We load extension add-ons.
    _getAndLoadLandingAddons({ addonType });

    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    dispatch.mockClear();

    // We request extension add-ons again.
    await changeLocation({
      history,
      pathname: getLocation(addonType),
    });

    expect(dispatch).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: GET_LANDING }),
    );
  });

  it('does not dispatch getLanding while loading', () => {
    store.dispatch(
      getLanding({
        addonType: ADDON_TYPE_EXTENSION,
        errorHandlerId,
      }),
    );
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: GET_LANDING }),
    );
  });

  it('does not dispatch getLanding when there is an error', () => {
    createFailedErrorHandler({
      id: errorHandlerId,
      store,
    });
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: GET_LANDING }),
    );
  });

  it('renders an error', () => {
    const message = 'Some error message';
    createFailedErrorHandler({
      id: errorHandlerId,
      message,
      store,
    });
    render();

    expect(screen.getByText(message)).toBeInTheDocument();
  });

  it('renders a LandingPage with no addons set', () => {
    render();

    expect(
      screen.getByText(
        `Explore powerful tools and features to customize Firefox and make ` +
          `the browser all your own.`,
      ),
    ).toBeInTheDocument();
  });

  // See https://github.com/mozilla/addons-frontend/issues/11788
  it('can render a LandingPage with a mixed case visibleAddonType in the URL', () => {
    render({ location: `/${lang}/${clientApp}/Extensions/` });

    expect(
      screen.getByText(
        `Explore powerful tools and features to customize Firefox and make ` +
          `the browser all your own.`,
      ),
    ).toBeInTheDocument();
  });

  it('renders a link to all categories', () => {
    render();

    expect(
      screen.getByRole('link', { name: 'Explore all categories' }),
    ).toBeInTheDocument();
  });

  it('does not render a theme class name when page type is extensions', () => {
    render({ addonType: ADDON_TYPE_EXTENSION });

    expect(screen.getByClassName('LandingPage')).not.toHaveClass(
      'LandingPage--theme',
    );
  });

  it('renders a theme class name when page type is themes', () => {
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    expect(screen.getByClassName('LandingPage')).toHaveClass(
      'LandingPage--theme',
    );
  });

  it.each([ADDON_TYPE_EXTENSION, ADDON_TYPE_STATIC_THEME])(
    'sets the links in each footer for %s',
    (addonType) => {
      const numberOfAddons =
        addonType === ADDON_TYPE_EXTENSION
          ? LANDING_PAGE_EXTENSION_COUNT
          : LANDING_PAGE_THEME_COUNT;
      const typeName =
        addonType === ADDON_TYPE_EXTENSION ? 'extensions' : 'themes';
      _getAndLoadLandingAddons({ addonType, numberOfAddons });
      render({ addonType });

      expect(
        screen.getByRole('link', { name: `See more recommended ${typeName}` }),
      ).toHaveAttribute(
        'href',
        [
          `/${lang}/${clientApp}/search/?promoted=${RECOMMENDED}`,
          `sort=${SEARCH_SORT_RANDOM}`,
          `type=${addonType}`,
        ].join('&'),
      );

      const expectedHref = (sort) =>
        addonType === ADDON_TYPE_EXTENSION
          ? [
              `/${lang}/${clientApp}/search/?promoted=${RECOMMENDED}`,
              `sort=${sort}`,
              `type=${addonType}`,
            ].join('&')
          : [
              `/${lang}/${clientApp}/search/?sort=${sort}`,
              `type=${addonType}`,
            ].join('&');

      expect(
        screen.getByRole('link', { name: `See more top rated ${typeName}` }),
      ).toHaveAttribute('href', expectedHref(SEARCH_SORT_TOP_RATED));

      expect(
        screen.getByRole('link', { name: `See more trending ${typeName}` }),
      ).toHaveAttribute('href', expectedHref(SEARCH_SORT_TRENDING));
    },
  );

  it('passes an isTheme prop as true if type is a theme', () => {
    _getAndLoadLandingAddons({ addonType: ADDON_TYPE_STATIC_THEME });
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    expect(screen.getAllByClassName('LandingAddonsCard-Themes')).toHaveLength(
      3,
    );
  });

  it('passes an isTheme prop as false if type is an extension', () => {
    _getAndLoadLandingAddons({ addonType: ADDON_TYPE_EXTENSION });
    render({ addonType: ADDON_TYPE_EXTENSION });

    expect(
      screen.queryByClassName('LandingAddonsCard-Themes'),
    ).not.toBeInTheDocument();
  });

  it('renders a LandingPage with themes HTML', () => {
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    expect(
      screen.getByText(
        `Change your browser's appearance. Choose from ` +
          `thousands of themes to give Firefox the look you want.`,
      ),
    ).toBeInTheDocument();
  });

  it('renders each add-on when set', async () => {
    const addonType = ADDON_TYPE_STATIC_THEME;
    render({ addonType });
    store.dispatch(
      loadLanding({
        addonType,
        recommended: createAddonsApiResult([
          { ...fakeAddon, name: createLocalizedString('Howdy'), slug: 'howdy' },
          {
            ...fakeAddon,
            name: createLocalizedString('Howdy again'),
            slug: 'howdy-again',
            type: addonType,
          },
          {
            ...fakeAddon,
            name: createLocalizedString('Howdy 2'),
            slug: 'howdy-2',
            type: addonType,
          },
          {
            ...fakeAddon,
            name: createLocalizedString('Howdy again 2'),
            slug: 'howdy-again-2',
            type: addonType,
          },
        ]),
        highlyRated: createAddonsApiResult([
          {
            ...fakeAddon,
            name: createLocalizedString('High'),
            slug: 'high',
            type: addonType,
          },

          {
            ...fakeAddon,
            name: createLocalizedString('High again'),
            slug: 'high-again',
            type: addonType,
          },
        ]),
        trending: createAddonsApiResult([
          {
            ...fakeAddon,
            name: createLocalizedString('Pop'),
            slug: 'pop',
            type: addonType,
          },
          {
            ...fakeAddon,
            name: createLocalizedString('Pop again'),
            slug: 'pop-again',
            type: addonType,
          },
        ]),
      }),
    );

    const expectNameLink = async (name) =>
      expect(await screen.findByRole('link', { name })).toBeInTheDocument();

    // recommended
    for (const name of ['Howdy', 'Howdy again', 'Howdy 2', 'Howdy again 2']) {
      await expectNameLink(name);
    }
    // highly rated
    for (const name of ['High', 'High again']) {
      await expectNameLink(name);
    }
    // trending
    for (const name of ['Pop', 'Pop again']) {
      await expectNameLink(name);
    }
  });

  it('dispatches getLanding when category filter is set', () => {
    const addonType = ADDON_TYPE_EXTENSION;

    // This loads a set of add-ons for a category.
    store.dispatch(
      getLanding({
        addonType,
        category: 'some-category',
        errorHandlerId,
      }),
    );
    store.dispatch(
      loadLanding({
        addonType,
        recommended: createAddonsApiResult([]),
        highlyRated: createAddonsApiResult([]),
        trending: createAddonsApiResult([]),
      }),
    );

    const dispatch = jest.spyOn(store, 'dispatch');
    render({ addonType });

    expect(dispatch).toHaveBeenCalledWith(setViewContext(ADDON_TYPE_EXTENSION));
    expect(dispatch).toHaveBeenCalledWith(
      getLanding({ addonType, errorHandlerId }),
    );
  });

  it('does not dispatch setViewContext when addonType does not change', async () => {
    const addonType = ADDON_TYPE_EXTENSION;
    store.dispatch(getLanding({ addonType, errorHandlerId }));
    store.dispatch(setViewContext(addonType));
    const dispatch = jest.spyOn(store, 'dispatch');
    render({ addonType });

    dispatch.mockClear();

    await changeLocation({
      history,
      pathname: getLocation(addonType),
    });

    expect(dispatch).not.toHaveBeenCalledWith(setViewContext(addonType));
  });

  it('renders an HTML title for themes', async () => {
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    await waitFor(() =>
      expect(getElement('title')).toHaveTextContent('Themes'),
    );
  });

  it('renders an HTML title for extensions', async () => {
    render();

    await waitFor(() =>
      expect(getElement('title')).toHaveTextContent('Extensions'),
    );
  });

  it('hides the trending shelf when there are no add-ons for it', () => {
    _getAndLoadLandingAddons({ trending: createAddonsApiResult([]) });
    render();

    expect(screen.getByText('Recommended extensions')).toBeInTheDocument();
    expect(screen.getByText('Top rated extensions')).toBeInTheDocument();
    expect(screen.queryByText('Trending extensions')).not.toBeInTheDocument();
  });

  it('hides the recommended shelf when there are no add-ons for it', () => {
    _getAndLoadLandingAddons({ recommended: createAddonsApiResult([]) });
    render();

    expect(
      screen.queryByText('Recommended extensions'),
    ).not.toBeInTheDocument();
    expect(screen.getByText('Top rated extensions')).toBeInTheDocument();
    expect(screen.getByText('Trending extensions')).toBeInTheDocument();
  });

  it('hides the highly rated shelf when there are no add-ons for it', () => {
    _getAndLoadLandingAddons({ highlyRated: createAddonsApiResult([]) });
    render();

    expect(screen.getByText('Recommended extensions')).toBeInTheDocument();
    expect(screen.queryByText('Top rated extensions')).not.toBeInTheDocument();
    expect(screen.getByText('Trending extensions')).toBeInTheDocument();
  });

  it.each([
    [ADDON_TYPE_EXTENSION, 'Download Firefox Extensions to add features'],
    [ADDON_TYPE_STATIC_THEME, 'Download themes to change how Firefox looks.'],
  ])(
    'renders a HeadMetaTags component for %s',
    async (addonType, description) => {
      render({ addonType });

      await waitFor(() =>
        expect(getElement('meta[name="description"]')).toHaveAttribute(
          'content',
          expect.stringContaining(description),
        ),
      );
    },
  );

  it('renders a HeadLinks component', async () => {
    render();

    await waitFor(() =>
      expect(getElement('link[rel="canonical"]')).toHaveAttribute(
        'href',
        getCanonicalURL({ locationPathname: getLocation() }),
      ),
    );
  });

  // This is an integration test between LandingPage and withExperiment.
  it('calls setDimension for the configured experiment', async () => {
    render();

    await waitFor(() => expect(tracking.setDimension).toHaveBeenCalledTimes(2));
    expect(tracking.setDimension).toHaveBeenCalledWith({
      dimension: EXPERIMENT_ID_GA_DIMENSION,
      value: EXPERIMENT_CONFIG.id,
    });
    expect(tracking.setDimension).toHaveBeenCalledWith(
      expect.objectContaining({
        dimension: EXPERIMENT_VARIATION_GA_DIMENSION,
      }),
    );
  });
});
<commit_msg>Another attempt to fix the intermittent failure in TestLandingPage.js (#11842)<commit_after>import { waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import { setViewContext } from 'amo/actions/viewContext';
import { EXPERIMENT_CONFIG } from 'amo/experiments/20220908_amo_dimension_test';
import { GET_LANDING, getLanding, loadLanding } from 'amo/reducers/landing';
import {
  ADDON_TYPE_EXTENSION,
  ADDON_TYPE_STATIC_THEME,
  CLIENT_APP_FIREFOX,
  LANDING_PAGE_EXTENSION_COUNT,
  LANDING_PAGE_THEME_COUNT,
  RECOMMENDED,
  SEARCH_SORT_RANDOM,
  SEARCH_SORT_TRENDING,
  SEARCH_SORT_TOP_RATED,
} from 'amo/constants';
import tracking from 'amo/tracking';
import { getCanonicalURL, visibleAddonType } from 'amo/utils';
import {
  EXPERIMENT_ID_GA_DIMENSION,
  EXPERIMENT_VARIATION_GA_DIMENSION,
} from 'amo/withExperiment';
import {
  changeLocation,
  createAddonsApiResult,
  createFailedErrorHandler,
  createLocalizedString,
  dispatchClientMetadata,
  fakeAddon,
  getElement,
  renderPage as defaultRender,
  screen,
} from 'tests/unit/helpers';

jest.mock('amo/tracking', () => ({
  ...jest.requireActual('amo/tracking'),
  sendEvent: jest.fn(),
  setDimension: jest.fn(),
}));

describe(__filename, () => {
  const clientApp = CLIENT_APP_FIREFOX;
  const lang = 'en-US';
  const errorHandlerId = 'LandingPage';
  const getLocation = (addonType = ADDON_TYPE_EXTENSION) =>
    `/${lang}/${clientApp}/${visibleAddonType(addonType)}/`;
  let history;
  let store;

  beforeEach(() => {
    store = dispatchClientMetadata({ clientApp, lang }).store;
  });

  afterEach(() => {
    jest.clearAllMocks().resetModules();
  });

  const render = ({ addonType = ADDON_TYPE_EXTENSION, location } = {}) => {
    const renderResults = defaultRender({
      initialEntries: [location || getLocation(addonType)],
      store,
    });
    history = renderResults.history;
    return renderResults;
  };

  const _getAndLoadLandingAddons = ({
    addonType = ADDON_TYPE_EXTENSION,
    numberOfAddons = 1,
    ...otherParams
  } = {}) => {
    store.dispatch(
      getLanding({
        addonType,
        errorHandlerId,
      }),
    );
    store.dispatch(
      loadLanding({
        addonType,
        recommended: createAddonsApiResult(
          Array(numberOfAddons).fill({
            ...fakeAddon,
            name: createLocalizedString('Featured'),
            slug: 'recommended',
            type: addonType,
          }),
        ),
        highlyRated: createAddonsApiResult(
          Array(numberOfAddons).fill({
            ...fakeAddon,
            name: createLocalizedString('High'),
            slug: 'high',
            type: addonType,
          }),
        ),
        trending: createAddonsApiResult(
          Array(numberOfAddons).fill({
            ...fakeAddon,
            name: createLocalizedString('Trending'),
            slug: 'trending',
            type: addonType,
          }),
        ),
        ...otherParams,
      }),
    );
  };

  it('dispatches setViewContext on load and update', async () => {
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).toHaveBeenCalledWith(setViewContext(ADDON_TYPE_EXTENSION));

    dispatch.mockClear();

    await changeLocation({
      history,
      pathname: getLocation(ADDON_TYPE_STATIC_THEME),
    });

    expect(dispatch).toHaveBeenCalledWith(
      setViewContext(ADDON_TYPE_STATIC_THEME),
    );
  });

  it('dispatches getLanding when results are not loaded', () => {
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).toHaveBeenCalledWith(
      getLanding({
        addonType: ADDON_TYPE_EXTENSION,
        errorHandlerId,
      }),
    );
  });

  it('dispatches getLanding when addon type changes', async () => {
    // We load theme add-ons.
    _getAndLoadLandingAddons({ addonType: ADDON_TYPE_STATIC_THEME });
    store.dispatch(setViewContext(ADDON_TYPE_STATIC_THEME));
    const dispatch = jest.spyOn(store, 'dispatch');
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    dispatch.mockClear();

    // Now we request extension add-ons.
    await changeLocation({
      history,
      pathname: getLocation(ADDON_TYPE_EXTENSION),
    });

    expect(dispatch).toHaveBeenCalledWith(
      getLanding({
        addonType: ADDON_TYPE_EXTENSION,
        errorHandlerId,
      }),
    );
    expect(dispatch).toHaveBeenCalledWith(setViewContext(ADDON_TYPE_EXTENSION));
  });

  it('does not dispatch getLanding when addon type does not change', async () => {
    const addonType = ADDON_TYPE_EXTENSION;

    // We load extension add-ons.
    _getAndLoadLandingAddons({ addonType });

    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    dispatch.mockClear();

    // We request extension add-ons again.
    await changeLocation({
      history,
      pathname: getLocation(addonType),
    });

    expect(dispatch).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: GET_LANDING }),
    );
  });

  it('does not dispatch getLanding while loading', () => {
    store.dispatch(
      getLanding({
        addonType: ADDON_TYPE_EXTENSION,
        errorHandlerId,
      }),
    );
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: GET_LANDING }),
    );
  });

  it('does not dispatch getLanding when there is an error', () => {
    createFailedErrorHandler({
      id: errorHandlerId,
      store,
    });
    const dispatch = jest.spyOn(store, 'dispatch');
    render();

    expect(dispatch).not.toHaveBeenCalledWith(
      expect.objectContaining({ type: GET_LANDING }),
    );
  });

  it('renders an error', () => {
    const message = 'Some error message';
    createFailedErrorHandler({
      id: errorHandlerId,
      message,
      store,
    });
    render();

    expect(screen.getByText(message)).toBeInTheDocument();
  });

  it('renders a LandingPage with no addons set', () => {
    render();

    expect(
      screen.getByText(
        `Explore powerful tools and features to customize Firefox and make ` +
          `the browser all your own.`,
      ),
    ).toBeInTheDocument();
  });

  // See https://github.com/mozilla/addons-frontend/issues/11788
  it('can render a LandingPage with a mixed case visibleAddonType in the URL', () => {
    render({ location: `/${lang}/${clientApp}/Extensions/` });

    expect(
      screen.getByText(
        `Explore powerful tools and features to customize Firefox and make ` +
          `the browser all your own.`,
      ),
    ).toBeInTheDocument();
  });

  it('renders a link to all categories', () => {
    render();

    expect(
      screen.getByRole('link', { name: 'Explore all categories' }),
    ).toBeInTheDocument();
  });

  it('does not render a theme class name when page type is extensions', () => {
    render({ addonType: ADDON_TYPE_EXTENSION });

    expect(screen.getByClassName('LandingPage')).not.toHaveClass(
      'LandingPage--theme',
    );
  });

  it('renders a theme class name when page type is themes', () => {
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    expect(screen.getByClassName('LandingPage')).toHaveClass(
      'LandingPage--theme',
    );
  });

  it.each([ADDON_TYPE_EXTENSION, ADDON_TYPE_STATIC_THEME])(
    'sets the links in each footer for %s',
    (addonType) => {
      const numberOfAddons =
        addonType === ADDON_TYPE_EXTENSION
          ? LANDING_PAGE_EXTENSION_COUNT
          : LANDING_PAGE_THEME_COUNT;
      const typeName =
        addonType === ADDON_TYPE_EXTENSION ? 'extensions' : 'themes';
      _getAndLoadLandingAddons({ addonType, numberOfAddons });
      render({ addonType });

      expect(
        screen.getByRole('link', { name: `See more recommended ${typeName}` }),
      ).toHaveAttribute(
        'href',
        [
          `/${lang}/${clientApp}/search/?promoted=${RECOMMENDED}`,
          `sort=${SEARCH_SORT_RANDOM}`,
          `type=${addonType}`,
        ].join('&'),
      );

      const expectedHref = (sort) =>
        addonType === ADDON_TYPE_EXTENSION
          ? [
              `/${lang}/${clientApp}/search/?promoted=${RECOMMENDED}`,
              `sort=${sort}`,
              `type=${addonType}`,
            ].join('&')
          : [
              `/${lang}/${clientApp}/search/?sort=${sort}`,
              `type=${addonType}`,
            ].join('&');

      expect(
        screen.getByRole('link', { name: `See more top rated ${typeName}` }),
      ).toHaveAttribute('href', expectedHref(SEARCH_SORT_TOP_RATED));

      expect(
        screen.getByRole('link', { name: `See more trending ${typeName}` }),
      ).toHaveAttribute('href', expectedHref(SEARCH_SORT_TRENDING));
    },
  );

  it('passes an isTheme prop as true if type is a theme', () => {
    _getAndLoadLandingAddons({ addonType: ADDON_TYPE_STATIC_THEME });
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    expect(screen.getAllByClassName('LandingAddonsCard-Themes')).toHaveLength(
      3,
    );
  });

  it('passes an isTheme prop as false if type is an extension', () => {
    _getAndLoadLandingAddons({ addonType: ADDON_TYPE_EXTENSION });
    render({ addonType: ADDON_TYPE_EXTENSION });

    expect(
      screen.queryByClassName('LandingAddonsCard-Themes'),
    ).not.toBeInTheDocument();
  });

  it('renders a LandingPage with themes HTML', () => {
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    expect(
      screen.getByText(
        `Change your browser's appearance. Choose from ` +
          `thousands of themes to give Firefox the look you want.`,
      ),
    ).toBeInTheDocument();
  });

  it('renders each add-on when set', async () => {
    const addonType = ADDON_TYPE_STATIC_THEME;
    render({ addonType });
    store.dispatch(
      loadLanding({
        addonType,
        recommended: createAddonsApiResult([
          { ...fakeAddon, name: createLocalizedString('Howdy'), slug: 'howdy' },
          {
            ...fakeAddon,
            name: createLocalizedString('Howdy again'),
            slug: 'howdy-again',
            type: addonType,
          },
          {
            ...fakeAddon,
            name: createLocalizedString('Howdy 2'),
            slug: 'howdy-2',
            type: addonType,
          },
          {
            ...fakeAddon,
            name: createLocalizedString('Howdy again 2'),
            slug: 'howdy-again-2',
            type: addonType,
          },
        ]),
        highlyRated: createAddonsApiResult([
          {
            ...fakeAddon,
            name: createLocalizedString('High'),
            slug: 'high',
            type: addonType,
          },

          {
            ...fakeAddon,
            name: createLocalizedString('High again'),
            slug: 'high-again',
            type: addonType,
          },
        ]),
        trending: createAddonsApiResult([
          {
            ...fakeAddon,
            name: createLocalizedString('Pop'),
            slug: 'pop',
            type: addonType,
          },
          {
            ...fakeAddon,
            name: createLocalizedString('Pop again'),
            slug: 'pop-again',
            type: addonType,
          },
        ]),
      }),
    );

    const expectNameLink = async (name) =>
      expect(await screen.findByRole('link', { name })).toBeInTheDocument();

    // recommended
    for (const name of ['Howdy', 'Howdy again', 'Howdy 2', 'Howdy again 2']) {
      await expectNameLink(name);
    }
    // highly rated
    for (const name of ['High', 'High again']) {
      await expectNameLink(name);
    }
    // trending
    for (const name of ['Pop', 'Pop again']) {
      await expectNameLink(name);
    }
  });

  it('dispatches getLanding when category filter is set', () => {
    const addonType = ADDON_TYPE_EXTENSION;

    // This loads a set of add-ons for a category.
    store.dispatch(
      getLanding({
        addonType,
        category: 'some-category',
        errorHandlerId,
      }),
    );
    store.dispatch(
      loadLanding({
        addonType,
        recommended: createAddonsApiResult([]),
        highlyRated: createAddonsApiResult([]),
        trending: createAddonsApiResult([]),
      }),
    );

    const dispatch = jest.spyOn(store, 'dispatch');
    render({ addonType });

    expect(dispatch).toHaveBeenCalledWith(setViewContext(ADDON_TYPE_EXTENSION));
    expect(dispatch).toHaveBeenCalledWith(
      getLanding({ addonType, errorHandlerId }),
    );
  });

  it('does not dispatch setViewContext when addonType does not change', async () => {
    const addonType = ADDON_TYPE_EXTENSION;
    store.dispatch(getLanding({ addonType, errorHandlerId }));
    store.dispatch(setViewContext(addonType));
    const dispatch = jest.spyOn(store, 'dispatch');
    render({ addonType });

    dispatch.mockClear();

    await changeLocation({
      history,
      pathname: getLocation(addonType),
    });

    expect(dispatch).not.toHaveBeenCalledWith(setViewContext(addonType));
  });

  it('renders an HTML title for themes', async () => {
    render({ addonType: ADDON_TYPE_STATIC_THEME });

    await waitFor(() =>
      expect(getElement('title')).toHaveTextContent('Themes'),
    );
  });

  it('renders an HTML title for extensions', async () => {
    render();

    await waitFor(() =>
      expect(getElement('title')).toHaveTextContent('Extensions'),
    );
  });

  it('hides the trending shelf when there are no add-ons for it', () => {
    _getAndLoadLandingAddons({ trending: createAddonsApiResult([]) });
    render();

    expect(screen.getByText('Recommended extensions')).toBeInTheDocument();
    expect(screen.getByText('Top rated extensions')).toBeInTheDocument();
    expect(screen.queryByText('Trending extensions')).not.toBeInTheDocument();
  });

  it('hides the recommended shelf when there are no add-ons for it', () => {
    _getAndLoadLandingAddons({ recommended: createAddonsApiResult([]) });
    render();

    expect(
      screen.queryByText('Recommended extensions'),
    ).not.toBeInTheDocument();
    expect(screen.getByText('Top rated extensions')).toBeInTheDocument();
    expect(screen.getByText('Trending extensions')).toBeInTheDocument();
  });

  it('hides the highly rated shelf when there are no add-ons for it', () => {
    _getAndLoadLandingAddons({ highlyRated: createAddonsApiResult([]) });
    render();

    expect(screen.getByText('Recommended extensions')).toBeInTheDocument();
    expect(screen.queryByText('Top rated extensions')).not.toBeInTheDocument();
    expect(screen.getByText('Trending extensions')).toBeInTheDocument();
  });

  it.each([
    [ADDON_TYPE_EXTENSION, 'Download Firefox Extensions to add features'],
    [ADDON_TYPE_STATIC_THEME, 'Download themes to change how Firefox looks.'],
  ])(
    'renders a HeadMetaTags component for %s',
    async (addonType, description) => {
      render({ addonType });

      await waitFor(() =>
        expect(getElement('meta[name="description"]')).toHaveAttribute(
          'content',
          expect.stringContaining(description),
        ),
      );
    },
  );

  it('renders a HeadLinks component', async () => {
    render();

    await waitFor(() =>
      expect(getElement('link[rel="canonical"]')).toHaveAttribute(
        'href',
        getCanonicalURL({ locationPathname: getLocation() }),
      ),
    );
  });

  // This is an integration test between LandingPage and withExperiment.
  it('calls setDimension for the configured experiment', async () => {
    render();

    // Sometimes the test checks for `setDimension` before it's been called.
    // Let's navigate to a different page before checking for the call.
    await userEvent.click(
      await screen.findByRole('link', { name: 'Explore all categories' }),
    );

    await waitFor(() => expect(tracking.setDimension).toHaveBeenCalledTimes(2));
    expect(tracking.setDimension).toHaveBeenCalledWith({
      dimension: EXPERIMENT_ID_GA_DIMENSION,
      value: EXPERIMENT_CONFIG.id,
    });
    expect(tracking.setDimension).toHaveBeenCalledWith(
      expect.objectContaining({
        dimension: EXPERIMENT_VARIATION_GA_DIMENSION,
      }),
    );
  });
});
<|endoftext|>


<commit_before>var should = require('should');
var helper = require('../helper');
var validate = require('bateeq-models').validator.inventory;
var manager;

function getData() {
    var expeditionDoc = {};
    var now = new Date();
    var stamp = now / 1000 | 0;
    var code = stamp.toString(36);

    expeditionDoc.code = code;
    expeditionDoc.date = now;
    expeditionDoc.sourceId = '57738435e8a64fc532cd5bf1';
    expeditionDoc.destinationId = '57738460d53dae9234ae0ae1';
    expeditionDoc.reference = `reference[${code}]`;
    expeditionDoc.spkDocuments = [ { spkDocumentId: '57a9ac2c1b31132b943bd6aa' } ];

    // var spk = {};
    // spk.spkDocumentId = '57a3018b9259e20a8840880f';
    //spk.spkDocument = { _id : "57a3018b9259e20a8840880f", items : [] }
    //spk.spkDocument.items.push( { articleVariantId: "578855c4964302281454fa51", quantity: 10 } )

    return expeditionDoc;
}

before('#00. connect db', function (done) {
    helper.getDb()
        .then(db => {
            var PusatBarangBaruKirimBarangJadiAksesorisManager = require('../../src/managers/inventory/efr-kb-exb-manager');
            manager = new PusatBarangBaruKirimBarangJadiAksesorisManager(db, {
                username: 'unit-test'
            });
            done();
        })
        .catch(e => {
            done(e);
        })
});

var createdId;
it('#01. should success when create new data', function(done) {
    var data = getData();
    manager.create(data)
        .then(id => {
            id.should.be.Object();
            createdId = id;
            done();
        })
        .catch(e => {
            done(e);
        })
});

var createdData;
it(`#02. should success when get created data with id`, function(done) {
    manager.getSingleByQuery({_id:createdId})
        .then(data => {
            createdData = data;
            done();
        })
        .catch(e => {
            done(e);
        })
});
 <commit_msg>update test<commit_after>var should = require('should');
var helper = require('../helper');
var validate = require('bateeq-models').validator.inventory;
var manager;

function getData() {
    var expeditionDoc = {};
    var now = new Date();
    var stamp = now / 1000 | 0;
    var code = stamp.toString(36);

    expeditionDoc.code = code;
    expeditionDoc.date = now;
    expeditionDoc.sourceId = '57738435e8a64fc532cd5bf1';
    expeditionDoc.destinationId = '57738460d53dae9234ae0ae1';
    expeditionDoc.reference = `reference[${code}]`;
    expeditionDoc.spkDocuments = [ { spkDocumentId: '57a9b5c5fd67c240cc3e6355' } ];

    // var spk = {};
    // spk.spkDocumentId = '57a3018b9259e20a8840880f';
    //spk.spkDocument = { _id : "57a3018b9259e20a8840880f", items : [] }
    //spk.spkDocument.items.push( { articleVariantId: "578855c4964302281454fa51", quantity: 10 } )

    return expeditionDoc;
}

before('#00. connect db', function (done) {
    helper.getDb()
        .then(db => {
            var PusatBarangBaruKirimBarangJadiAksesorisManager = require('../../src/managers/inventory/efr-kb-exb-manager');
            manager = new PusatBarangBaruKirimBarangJadiAksesorisManager(db, {
                username: 'unit-test'
            });
            done();
        })
        .catch(e => {
            done(e);
        })
});

var createdId;
it('#01. should success when create new data', function(done) {
    var data = getData();
    manager.create(data)
        .then(id => {
            id.should.be.Object();
            createdId = id;
            done();
        })
        .catch(e => {
            done(e);
        })
});

var createdData;
it(`#02. should success when get created data with id`, function(done) {
    manager.getSingleByQuery({_id:createdId})
        .then(data => {
            createdData = data;
            done();
        })
        .catch(e => {
            done(e);
        })
});
 <|endoftext|>


<commit_before>var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var webpack = require('webpack');

module.exports = {
  //devtool: 'source-map',
  entry: [
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'app.js',
    publicPath: ''
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Langa',
      template: './index.html'
    }),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      'screw-ie8': true,
      comments: false,
      compressor: {
        negate_iife: true,
        warnings: false,
        drop_console: true,
        unused: true,
        dead_code: true,
        unsafe: true,
        keep_fargs: false,
        pure_getters: true,
        join_vars: true,
        sequences: true,
        loops: true,
        booleans: true,
        evaluate: true,
        conditionals: true

      }
    })
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: path.join(__dirname, 'src')
    }, {
      test: /\.scss$/,
      loaders: [
        'style',
        'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
        'sass'
      ]
    }, {
      test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
      loader: 'file-loader?name=fonts/[name].[ext]'
    }, {
      test: /\.(png|jpg)$/,
      loader: 'url?limit=25000'
    }]
  }
};
<commit_msg>[style] remove var usage<commit_after>const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const webpack = require('webpack');

module.exports = {
  entry: [
    './src/index'
  ],
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'app.js',
    publicPath: ''
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: 'Langa',
      template: './index.html'
    }),
    new webpack.optimize.OccurenceOrderPlugin(),
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      'screw-ie8': true,
      comments: false,
      compressor: {
        negate_iife: true,
        warnings: false,
        drop_console: true,
        unused: true,
        dead_code: true,
        unsafe: true,
        keep_fargs: false,
        pure_getters: true,
        join_vars: true,
        sequences: true,
        loops: true,
        booleans: true,
        evaluate: true,
        conditionals: true

      }
    })
  ],
  module: {
    loaders: [{
      test: /\.js$/,
      loaders: ['babel'],
      include: path.join(__dirname, 'src')
    }, {
      test: /\.scss$/,
      loaders: [
        'style',
        'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]',
        'sass'
      ]
    }, {
      test: /\.(ttf|otf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
      loader: 'file-loader?name=fonts/[name].[ext]'
    }, {
      test: /\.(png|jpg)$/,
      loader: 'url?limit=25000'
    }]
  }
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014 brian@bevey.org
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * @author brian@bevey.org
 * @fileoverview Unit test for devices/sports/controller.js
 */

exports.sportsControllerTest = {
  fragments : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        fragments        = sportsController.fragments();

    test.strictEqual(typeof fragments.game,   'string', 'Fragment verified');
    test.strictEqual(typeof fragments.league, 'string', 'Fragment verified');

    test.done();
  },

  postPrepare : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        configGet        = { host    : 'example.com',
                             port    : '80',
                             path    : '/test/',
                             method  : 'GET',
                             badData : 'FAILURE' },
        testGet          = sportsController.postPrepare(configGet);

    test.deepEqual(testGet, {
                              host : 'example.com',
                              port : '80',
                              path : '/test/',
                              method : 'GET',
                              headers: {
                                'Accept'         : 'application/json',
                                'Accept-Charset' : 'utf-8',
                                'User-Agent'     : 'node-switchBoard'
                              }
                            }, 'Additional params are filtered out.');

    test.done();
  },

  getStatus : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        goodStatus       = sportsController.getStatus('in'),
        badStatus        = sportsController.getStatus('foo');

    test.strictEqual(goodStatus, 'LIVE',    'Status converted');
    test.strictEqual(badStatus,  'UNKNOWN', 'Status sanitized');

    test.done();
  },

  getGames : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        sportsData       = [{
                             name    : 'quidditch',
                             leagues : [{
                               abbreviation : 'nq',
                               name         : 'National quidditch',
                               shortName    : 'quidditch',
                               events       : [{
                                 date        : '2016-02-05T00:00:00Z',
                                 location    : 'Azkaban',
                                 status      : 'post',
                                 summary     : 'Text',
                                 link        : 'http://localhost',
                                 competitors : [{
                                   type   : 'team',
                                   score  : '111',
                                   winner : true,
                                   name   : 'Gryffindor',
                                   logo   : 'http://localhost/team-1.png',
                                   test   : true
                                 }, {
                                   type   : 'team',
                                   score  : '110',
                                   winner : false,
                                   name   : 'Slytherin',
                                   logo   : 'http://localhost/team-2.png',
                                   test   : true
                                 }]
                               }, {
                                 date        : '2016-02-05T02:00:00Z',
                                 location    : 'Azkaban',
                                 status      : 'post',
                                 summary     : 'Text',
                                 link        : 'http://localhost',
                                 competitors : [{
                                   type   : 'team',
                                   score  : '2',
                                   winner : false,
                                   name   : 'Foo',
                                   logo   : 'http://localhost/team-3.png',
                                   test   : true
                                 }, {
                                   type   : 'team',
                                   score  : '3',
                                   winner : true,
                                   name   : 'Bar',
                                   logo   : 'http://localhost/team-4.png',
                                   test   : true
                                 }]
                               }]
                             }]
                           }],
        badData          = {},
        testSportsData   = sportsController.getGames(sportsData, 'TEST'),
        testBadData      = sportsController.getGames(badData,    'TEST');

    test.deepEqual(testSportsData, { nq: {
                                       name: 'quidditch',
                                       title: 'National quidditch',
                                       games:
                                        [ { home:
                                             { title: 'Gryffindor',
                                               score: '111',
                                               winner: true,
                                               image: '' },
                                            away:
                                             { title: 'Slytherin',
                                               score: '110',
                                               winner: false,
                                               image: '' },
                                            time: 1454630400000,
                                            summary: 'Text',
                                            status: 'FINAL',
                                            location: 'Azkaban',
                                            url: 'http://localhost' },
                                          { home:
                                             { title: 'Foo',
                                               score: '2',
                                               winner: false,
                                               image: '' },
                                            away:
                                             { title: 'Bar',
                                               score: '3',
                                               winner: true,
                                               image: '' },
                                            time: 1454637600000,
                                            summary: 'Text',
                                            status: 'FINAL',
                                            location: 'Azkaban',
                                            url: 'http://localhost' } ] } }, 'Grabs game data');
    test.deepEqual(testBadData, {}, 'Nothing should be returned for bad data');

    test.done();
  }
};
<commit_msg>Forgot to include test change<commit_after>/**
 * Copyright (c) 2014 brian@bevey.org
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

/**
 * @author brian@bevey.org
 * @fileoverview Unit test for devices/sports/controller.js
 */

exports.sportsControllerTest = {
  fragments : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        fragments        = sportsController.fragments();

    test.strictEqual(typeof fragments.game,   'string', 'Fragment verified');
    test.strictEqual(typeof fragments.league, 'string', 'Fragment verified');

    test.done();
  },

  postPrepare : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        configGet        = { host    : 'example.com',
                             port    : '80',
                             path    : '/test/',
                             method  : 'GET',
                             badData : 'FAILURE' },
        testGet          = sportsController.postPrepare(configGet);

    test.deepEqual(testGet, {
                              host : 'example.com',
                              port : '80',
                              path : '/test/',
                              method : 'GET',
                              headers: {
                                'Accept'         : 'application/json',
                                'Accept-Charset' : 'utf-8',
                                'User-Agent'     : 'node-switchBoard'
                              }
                            }, 'Additional params are filtered out.');

    test.done();
  },

  getStatus : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        goodStatus       = sportsController.getStatus('in'),
        badStatus        = sportsController.getStatus('foo');

    test.strictEqual(goodStatus, 'LIVE',    'Status converted');
    test.strictEqual(badStatus,  'UNKNOWN', 'Status sanitized');

    test.done();
  },

  getGames : function (test) {
    'use strict';

    var sportsController = require(__dirname + '/../../../../devices/sports/controller'),
        sportsData       = [{
                             name    : 'quidditch',
                             leagues : [{
                               abbreviation : 'nq',
                               name         : 'National quidditch',
                               shortName    : 'quidditch',
                               events       : [{
                                 date        : '2016-02-05T00:00:00Z',
                                 location    : 'Azkaban',
                                 status      : 'post',
                                 summary     : 'Text',
                                 links       : [{ href: 'http://localhost' }],
                                 competitors : [{
                                   type   : 'team',
                                   score  : '111',
                                   winner : true,
                                   name   : 'Gryffindor',
                                   logo   : 'http://localhost/team-1.png',
                                   test   : true
                                 }, {
                                   type   : 'team',
                                   score  : '110',
                                   winner : false,
                                   name   : 'Slytherin',
                                   logo   : 'http://localhost/team-2.png',
                                   test   : true
                                 }]
                               }, {
                                 date        : '2016-02-05T02:00:00Z',
                                 location    : 'Azkaban',
                                 status      : 'post',
                                 summary     : 'Text',
                                 links       : [{ href: 'http://localhost' }],
                                 competitors : [{
                                   type   : 'team',
                                   score  : '2',
                                   winner : false,
                                   name   : 'Foo',
                                   logo   : 'http://localhost/team-3.png',
                                   test   : true
                                 }, {
                                   type   : 'team',
                                   score  : '3',
                                   winner : true,
                                   name   : 'Bar',
                                   logo   : 'http://localhost/team-4.png',
                                   test   : true
                                 }]
                               }]
                             }]
                           }],
        badData          = {},
        testSportsData   = sportsController.getGames(sportsData, 'TEST'),
        testBadData      = sportsController.getGames(badData,    'TEST');

    test.deepEqual(testSportsData, { nq: {
                                       name: 'quidditch',
                                       title: 'National quidditch',
                                       games:
                                        [ { home:
                                             { title: 'Gryffindor',
                                               score: '111',
                                               winner: true,
                                               image: '' },
                                            away:
                                             { title: 'Slytherin',
                                               score: '110',
                                               winner: false,
                                               image: '' },
                                            time: 1454630400000,
                                            summary: 'Text',
                                            status: 'FINAL',
                                            location: 'Azkaban',
                                            url: 'http://localhost' },
                                          { home:
                                             { title: 'Foo',
                                               score: '2',
                                               winner: false,
                                               image: '' },
                                            away:
                                             { title: 'Bar',
                                               score: '3',
                                               winner: true,
                                               image: '' },
                                            time: 1454637600000,
                                            summary: 'Text',
                                            status: 'FINAL',
                                            location: 'Azkaban',
                                            url: 'http://localhost' } ] } }, 'Grabs game data');
    test.deepEqual(testBadData, {}, 'Nothing should be returned for bad data');

    test.done();
  }
};
<|endoftext|>


<commit_before>const webpack = require('webpack');
const path = require('path');

module.exports = {
    devtool: 'source-map',
    entry: [
        path.join(__dirname, 'src', 'index.js')
    ],

    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: '/'
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: JSON.stringify('production')
            }
        }),
        new webpack.optimize.UglifyJsPlugin()
    ],

    module: {
        loaders: [
            {
                test: /\.js$/,
                loaders: [`babel?${JSON.stringify({
                    cacheDirectory: true,
                    presets: ['es2015', 'stage-0', 'react'],
                })}`],
                exclude: /node_modules/,
            },
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader',
            },
            {
                test: /\.png$/,
                loader: 'url-loader?limit=100000',
            },
            {
                test: /\.jpg$/,
                loader: 'file-loader',
            },
            {
                test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'url?limit=10000&mimetype=application/font-woff',
            },
            {
                test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'url?limit=10000&mimetype=application/octet-stream',
            },
            {
                test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file',
            },
            {
                test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'url?limit=10000&mimetype=image/svg+xml',
            },
        ],
    },
};
<commit_msg>Remove UglifyJsPlugin<commit_after>const webpack = require('webpack');
const path = require('path');

module.exports = {
    devtool: 'source-map',
    entry: [
        path.join(__dirname, 'src', 'index.js')
    ],

    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: '/'
    },
    plugins: [
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: JSON.stringify('production')
            }
        })
    ],

    module: {
        loaders: [
            {
                test: /\.js$/,
                loaders: [`babel?${JSON.stringify({
                    cacheDirectory: true,
                    presets: ['es2015', 'stage-0', 'react'],
                })}`],
                exclude: /node_modules/,
            },
            {
                test: /\.css$/,
                loader: 'style-loader!css-loader',
            },
            {
                test: /\.png$/,
                loader: 'url-loader?limit=100000',
            },
            {
                test: /\.jpg$/,
                loader: 'file-loader',
            },
            {
                test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'url?limit=10000&mimetype=application/font-woff',
            },
            {
                test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'url?limit=10000&mimetype=application/octet-stream',
            },
            {
                test: /\.eot(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'file',
            },
            {
                test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                loader: 'url?limit=10000&mimetype=image/svg+xml',
            },
        ],
    },
};
<|endoftext|>


<commit_before>process.env.NODE_ENV = 'test'

const path = require('path')
const webpack = require('webpack')
const testWebpackConfig = require('./frontend_build/baseWebpackConfig')

testWebpackConfig.entry = undefined

testWebpackConfig.plugins.push(new webpack.EnvironmentPlugin({
  JSPEC_PATH: null,
  JSPEC_GROUP: null,
  A11Y_REPORT: false
}))

// These externals are necessary for Enzyme
// See http://airbnb.io/enzyme/docs/guides/webpack.html
Object.assign(testWebpackConfig.externals || (testWebpackConfig.externals = {}), {
  'react-dom/server': 'window',
  'react/lib/ReactContext': 'true',
  'react/lib/ExecutionEnvironment': 'true',
  'react-dom/test-utils': 'somethingThatDoesntActuallyExist',
  'react-test-renderer/shallow': 'somethingThatDoesntActuallyExist'
})

testWebpackConfig.resolve.alias['spec/jsx'] = path.resolve(__dirname, 'spec/javascripts/jsx')

testWebpackConfig.module.rules.unshift({
  test: [
    /\/spec\/coffeescripts\//,
    /\/spec_canvas\/coffeescripts\//,
    // Some plugins use a special spec_canvas path for their specs
    /\/spec\/javascripts\/jsx\//,
    /\/ember\/.*\/tests\//
  ],

  // Our spec files expect qunit's global `test`, `module`, `asyncTest` and `start` variables.
  // These imports loaders make it so they are avalable as local variables
  // inside of a closure, without truly making them globals.
  // We should get rid of this and just change our actual source to s/test/qunit.test/ and s/module/qunit.module/
  loaders: [
    'imports-loader?test=>QUnit.test',
  ]
})

module.exports = testWebpackConfig
<commit_msg>Add SENTRY_URL to what webpack injects into the bundle<commit_after>process.env.NODE_ENV = 'test'

const path = require('path')
const webpack = require('webpack')
const testWebpackConfig = require('./frontend_build/baseWebpackConfig')

testWebpackConfig.entry = undefined

testWebpackConfig.plugins.push(new webpack.EnvironmentPlugin({
  JSPEC_PATH: null,
  JSPEC_GROUP: null,
  A11Y_REPORT: false,
  SENTRY_URL: null
}))

// These externals are necessary for Enzyme
// See http://airbnb.io/enzyme/docs/guides/webpack.html
Object.assign(testWebpackConfig.externals || (testWebpackConfig.externals = {}), {
  'react-dom/server': 'window',
  'react/lib/ReactContext': 'true',
  'react/lib/ExecutionEnvironment': 'true',
  'react-dom/test-utils': 'somethingThatDoesntActuallyExist',
  'react-test-renderer/shallow': 'somethingThatDoesntActuallyExist'
})

testWebpackConfig.resolve.alias['spec/jsx'] = path.resolve(__dirname, 'spec/javascripts/jsx')

testWebpackConfig.module.rules.unshift({
  test: [
    /\/spec\/coffeescripts\//,
    /\/spec_canvas\/coffeescripts\//,
    // Some plugins use a special spec_canvas path for their specs
    /\/spec\/javascripts\/jsx\//,
    /\/ember\/.*\/tests\//
  ],

  // Our spec files expect qunit's global `test`, `module`, `asyncTest` and `start` variables.
  // These imports loaders make it so they are avalable as local variables
  // inside of a closure, without truly making them globals.
  // We should get rid of this and just change our actual source to s/test/qunit.test/ and s/module/qunit.module/
  loaders: [
    'imports-loader?test=>QUnit.test',
  ]
})

module.exports = testWebpackConfig
<|endoftext|>