diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000000..2a2130d6e838 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,509 @@ +# Configuration file for https://circleci.com/gh/angular/angular.js + +# Note: YAML anchors allow an object to be re-used, reducing duplication. +# The ampersand declares an alias for an object, then later the `<<: *name` +# syntax dereferences it. +# See http://blog.daemonl.com/2016/02/yaml.html +# To validate changes, use an online parser, eg. +# http://yaml-online-parser.appspot.com/ + +# CircleCI configuration version +# Version 2.1 allows for extra config reuse features +# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse +version: 2.1 + +# Workspace persisted by the `setup` job to share build artifacts with other jobs. +# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs +# https://circleci.com/blog/deep-diving-into-circleci-workspaces/ +var_workspace_location: &workspace_location ~/ + +# Executor Definitions +# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors +# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version. +# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.) +executors: + default-executor: + parameters: + resource_class: + type: string + default: medium + docker: + - image: circleci/node:14.16.1@sha256:b094e85848b43209ca83d9bb114d406fe62c75cb73b18c9d8eb1a9c6462c97d4 + resource_class: << parameters.resource_class >> + working_directory: ~/ng + cloud-sdk: + description: The docker container to use when running gcp-gcs commands + docker: + - image: google/cloud-sdk:alpine@sha256:7d0cae28cb282b76f2d9babe278c63c910d54f0cceca7a65fdf6806e2b43882e + working_directory: ~/ng + + +# Filter Definitions + +# Filter to run a job on all branches and any `v1.X.Y(-Z)` tags. +# Since the jobs need to run on tagged builds too, a `tags` section has to be explicitly specified. +# (The `branches` section could be omitted, since it defaults to all branches - just being explicit +# here). +# See also https://circleci.com/docs/2.0/workflows/#executing-workflows-for-a-git-tag. +var-filter-run-always: &run-always + filters: + branches: + only: /.*/ + tags: + only: /v1\.\d+\.\d.*/ + +# Filter to run a job when code might need to be deployed - i.e. on builds for the `master` branch. +# (Further checks are needed to determine whether a deployment is actually needed, but these are not +# possible via filters.) +var-filter-run-on-master: &run-on-master + filters: + branches: + only: + - master + tags: + ignore: /.*/ + +# Filter to run a job when code/docs might need to be deployed - i.e. on tagged builds and on builds +# for master and `v1.*.x` branches. +# (Further checks are needed to determine whether a deployment is actually needed, but these are not +# possible via filters.) +var-filter-run-on-tags-and-master-and-version-branches: &run-on-tags-and-master-and-version-branches + filters: + branches: + only: + - master + - /v1\.\d+\.x/ + tags: + only: /v1\.\d+\.\d.*/ + +# Filter to run a job when docs might need to be deployed - i.e. on builds for `v1.*.x` branches, +# which might correspond to the stable branch. +# (Further checks are needed to determine whether a deployment is actually needed, but these are not +# possible via filters.) +var-filter-run-on-version-branches: &run-on-version-branches + filters: + branches: + only: + - /v1\.\d+\.x/ + tags: + ignore: /.*/ + + +# Command Definitions +# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands +commands: + skip_on_pr_and_fork_builds: + description: Skip a job on pull request and fork builds + steps: + - run: + name: Skip this job if this is a pull request or fork build + # Note: Using `CIRCLE_*` env variables (instead of those defined in `env.sh` so that this + # step can be run before `init_environment`. + command: > + if [[ -n "$CIRCLE_PR_NUMBER" ]] || + [[ "$CIRCLE_PROJECT_USERNAME" != "angular" ]] || + [[ "$CIRCLE_PROJECT_REPONAME" != "angular.js" ]]; then + echo "Skipping this job, because this is either a pull request or a fork build." + circleci step halt + fi + + skip_unless_stable_branch: + description: Skip a job unless this is the stable branch + steps: + - run: + name: Skip this job unless this is the stable branch + command: > + if [[ "$DIST_TAG" != "latest" ]]; then + echo "Skipping deployment, because this is not the stable branch." + circleci step halt + fi + + skip_unless_tag_or_master_or_stable_branch: + description: Skip a job unless this is a tag or the master or stable branch + steps: + - run: + name: Skip this job unless this is a tag or the master or stable branch + command: > + if [[ "$CI_GIT_TAG" == "false" ]] && + [[ "$CI_BRANCH" != "master" ]] && + [[ "$DIST_TAG" != "latest" ]]; then + echo "Skipping this job, because this is neither a tag nor the master or stable branch." + circleci step halt + fi + + + custom_attach_workspace: + description: Attach workspace at a predefined location + steps: + - attach_workspace: + at: *workspace_location + + # Java is needed for running the Closure Compiler (during the `minall` task). + install_java: + description: Install java + steps: + - run: + name: Install java + command: | + sudo apt-get update + # Install java runtime + sudo apt-get install default-jre + + # Initializes the CI environment by setting up common environment variables. + init_environment: + description: Initializing environment (setting up variables) + steps: + - run: + name: Set up environment + environment: + CIRCLE_GIT_BASE_REVISION: << pipeline.git.base_revision >> + CIRCLE_GIT_REVISION: << pipeline.git.revision >> + command: ./.circleci/env.sh + - run: + # Configure git as the CircleCI `checkout` command does. + # This is needed because we only checkout on the setup job. + # Add GitHub to known hosts + name: Configure git + command: | + mkdir -p ~/.ssh + echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts + git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true + git config --global gc.auto 0 || true + + init_saucelabs_environment: + description: Sets up a domain that resolves to the local host. + steps: + - run: + name: Preparing environment for running tests on Saucelabs. + command: | + # For SauceLabs jobs, we set up a domain which resolves to the machine which launched + # the tunnel. We do this because devices are sometimes not able to properly resolve + # `localhost` or `127.0.0.1` through the SauceLabs tunnel. Using a domain that does not + # resolve to anything on SauceLabs VMs ensures that such requests are always resolved + # through the tunnel, and resolve to the actual tunnel host machine (i.e. the CircleCI VM). + # More context can be found in: https://github.com/angular/angular/pull/35171. + setPublicVar SAUCE_LOCALHOST_ALIAS_DOMAIN "angular-ci.local" + setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev) + - run: + # Sets up a local domain in the machine's host file that resolves to the local + # host. This domain is helpful in Saucelabs tests where devices are not able to + # properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel. + name: Setting up alias domain for local host. + command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts + + start_saucelabs: + steps: + - run: + name: Starting Saucelabs tunnel service + command: ./lib/saucelabs/sauce-service.sh start-ready-wait + + stop_saucelabs: + steps: + - run: + name: Stopping Saucelabs tunnel service + command: ./lib/saucelabs/sauce-service.sh stop + + run_e2e_tests: + parameters: + specs: + type: string + steps: + - custom_attach_workspace + - init_environment + - init_saucelabs_environment + - start_saucelabs + - run: + command: yarn grunt test:circleci-protractor --specs="<< parameters.specs >>" + no_output_timeout: 30m + - stop_saucelabs + + run_e2e_tests_jquery: + parameters: + specs: + type: string + steps: + - custom_attach_workspace + - init_environment + - init_saucelabs_environment + - start_saucelabs + - run: + environment: + USE_JQUERY: 1 + command: yarn grunt test:circleci-protractor --specs="<< parameters.specs >>" + no_output_timeout: 30m + - stop_saucelabs + +# Job definitions +# Jobs can include parameters that are passed in the workflow job invocation. +# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs +jobs: + setup: + executor: default-executor + steps: + - checkout + - init_environment + - install_java + - run: + name: Running Yarn install + command: yarn install --frozen-lockfile --non-interactive + # Yarn's requests sometimes take more than 10mins to complete. + no_output_timeout: 45m + - run: yarn grunt package + # Persist any changes at this point to be reused by further jobs. + # **NOTE**: To add new content to the workspace, always persist on the same root. + - persist_to_workspace: + root: *workspace_location + paths: + - ./ng + + lint: + executor: default-executor + steps: + - custom_attach_workspace + - init_environment + - run: yarn grunt ci-checks + - run: yarn commitplease "$CI_COMMIT_RANGE" + - run: yarn grunt validate-angular-files + + unit-test: + executor: + name: default-executor + steps: + - custom_attach_workspace + - init_environment + - install_java + - init_saucelabs_environment + - run: yarn grunt test:promises-aplus + - run: + command: yarn grunt test:jqlite --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:modules --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:docs --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + + unit-test-jquery: + executor: + name: default-executor + steps: + - custom_attach_workspace + - init_environment + - init_saucelabs_environment + - run: + command: yarn grunt test:jquery --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:jquery-2.2 --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + - run: + command: yarn grunt test:jquery-2.1 --browsers="$BROWSERS" --reporters=spec + no_output_timeout: 10m + + e2e-test-1: + executor: + name: default-executor + steps: + - run_e2e_tests: + specs: test/e2e/tests/**/*.js + + e2e-test-2a: + executor: + name: default-executor + steps: + - run_e2e_tests: + specs: build/docs/ptore2e/example-ng*/**/default_test.js + + e2e-test-2b: + executor: + name: default-executor + steps: + - run_e2e_tests: + specs: "build/docs/ptore2e/!(example-ng*)/**/default_test.js" + + e2e-test-jquery-1: + executor: + name: default-executor + steps: + - run_e2e_tests_jquery: + specs: test/e2e/tests/**/*.js + + e2e-test-jquery-2a: + executor: + name: default-executor + steps: + - run_e2e_tests_jquery: + specs: build/docs/ptore2e/example-ng*/**/jquery_test.js + + e2e-test-jquery-2b: + executor: + name: default-executor + steps: + - run_e2e_tests_jquery: + specs: build/docs/ptore2e/!(example-ng*)/**/jquery_test.js + + prepare-deployment: + executor: + name: default-executor + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + - run: yarn grunt prepareDeploy + # Write the deployment files to the workspace to be used by deploy-docs and deploy-code + - persist_to_workspace: + root: *workspace_location + paths: + - ./ng + + # The `deploy-code-files` job should only run when all of these conditions are true for the build: + # - It is for the `angular/angular.js` repository (not a fork). + # - It is not for a pull request. + # - It is for a tag or the master branch or the stable branch(*). + # + # *: The stable branch is the one that has the value `latest` in `package.json > distTag`. + deploy-code-files: + executor: + name: cloud-sdk + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + - skip_unless_tag_or_master_or_stable_branch + - run: ls scripts/code.angularjs.org-firebase/deploy + - run: + name: Authenticate and configure Docker + command: | + echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=- + gcloud --quiet config set project ${GOOGLE_PROJECT_ID} + - run: + name: Sync files to code.angularjs.org + command: | + gsutil -m rsync -r scripts/code.angularjs.org-firebase/deploy gs://code-angularjs-org-338b8.appspot.com + + # The `deploy-code-firebase` job should only run when all of these conditions are true for the build: + # - It is for the `angular/angular.js` repository (not a fork). + # - It is not for a pull request. + # - It is for the master branch. + # (This is enforced via job filters, so we don't need to a step to check it here.) + deploy-code-firebase: + executor: + name: default-executor + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + # Install dependencies for Firebase functions to prevent parsing errors during deployment. + # See https://github.com/angular/angular.js/pull/16453. + - run: + name: Install dependencies in `scripts/code.angularjs.org-firebase/functions/`. + working_directory: scripts/code.angularjs.org-firebase/functions + command: yarn install --frozen-lockfile --ignore-engines --non-interactive + - run: + name: Deploy to Firebase from `scripts/code.angularjs.org-firebase/`. + working_directory: scripts/code.angularjs.org-firebase + command: | + # Do not use `yarn firebase` as that causes the Firebase CLI to look for `firebase.json` + # in the root directory, even if run from inside `scripts/code.angularjs.org-firebase/`. + firebase=$(yarn bin)/firebase + $firebase use + $firebase deploy --message "Commit:\ $CI_COMMIT" --non-interactive --token "$FIREBASE_TOKEN" + + # The `deploy-docs` job should only run when all of these conditions are true for the build: + # - It is for the `angular/angular.js` repository (not a fork). + # - It is not for a pull request. + # - It is for the stable branch(*). + # + # *: The stable branch is the one that has the value `latest` in `package.json > distTag`. + deploy-docs: + executor: + name: default-executor + steps: + - skip_on_pr_and_fork_builds + - custom_attach_workspace + - init_environment + - skip_unless_stable_branch + # Install dependencies for Firebase functions to prevent parsing errors during deployment. + # See https://github.com/angular/angular.js/pull/16453. + - run: + name: Install dependencies in `scripts/docs.angularjs.org-firebase/functions/`. + working_directory: scripts/docs.angularjs.org-firebase/functions + command: yarn install --frozen-lockfile --ignore-engines --non-interactive + - run: + name: Deploy to Firebase from `scripts/docs.angularjs.org-firebase/`. + working_directory: scripts/docs.angularjs.org-firebase + command: | + # Do not use `yarn firebase` as that causes the Firebase CLI to look for `firebase.json` + # in the root directory, even if run from inside `scripts/docs.angularjs.org-firebase/`. + firebase=$(yarn bin)/firebase + $firebase use + $firebase deploy --message "Commit:\ $CI_COMMIT" --non-interactive --token "$FIREBASE_TOKEN" + +workflows: + version: 2 + default_workflow: + jobs: + - setup: + <<: *run-always + - lint: + <<: *run-always + requires: + - setup + - unit-test: + <<: *run-always + requires: + - setup + - unit-test-jquery: + <<: *run-always + requires: + - setup + - e2e-test-1: + <<: *run-always + requires: + - setup + - e2e-test-2a: + <<: *run-always + requires: + - setup + - e2e-test-2b: + <<: *run-always + requires: + - setup + - e2e-test-jquery-1: + <<: *run-always + requires: + - setup + - e2e-test-jquery-2a: + <<: *run-always + requires: + - setup + - e2e-test-jquery-2b: + <<: *run-always + requires: + - setup + - prepare-deployment: + <<: *run-on-tags-and-master-and-version-branches + requires: + - setup + - lint + - unit-test + - unit-test-jquery + - e2e-test-1 + - e2e-test-2a + - e2e-test-2b + - e2e-test-jquery-1 + - e2e-test-jquery-2a + - e2e-test-jquery-2b + - deploy-code-files: + <<: *run-on-tags-and-master-and-version-branches + requires: + - prepare-deployment + - deploy-code-firebase: + <<: *run-on-master + requires: + - prepare-deployment + - deploy-docs: + <<: *run-on-version-branches + requires: + - prepare-deployment diff --git a/.circleci/env-helpers.inc.sh b/.circleci/env-helpers.inc.sh new file mode 100644 index 000000000000..5fa1263e112f --- /dev/null +++ b/.circleci/env-helpers.inc.sh @@ -0,0 +1,73 @@ +#################################################################################################### +# Helpers for defining environment variables for CircleCI. +# +# In CircleCI, each step runs in a new shell. The way to share ENV variables across steps is to +# export them from `$BASH_ENV`, which is automatically sourced at the beginning of every step (for +# the default `bash` shell). +# +# See also https://circleci.com/docs/2.0/env-vars/#using-bash_env-to-set-environment-variables. +#################################################################################################### + +# Set and print an environment variable. +# +# Use this function for setting environment variables that are public, i.e. it is OK for them to be +# visible to anyone through the CI logs. +# +# Usage: `setPublicVar ` +function setPublicVar() { + setSecretVar $1 "$2"; + echo "$1=$2"; +} + +# Set (without printing) an environment variable. +# +# Use this function for setting environment variables that are secret, i.e. should not be visible to +# everyone through the CI logs. +# +# Usage: `setSecretVar ` +function setSecretVar() { + # WARNING: Secrets (e.g. passwords, access tokens) should NOT be printed. + # (Keep original shell options to restore at the end.) + local -r originalShellOptions=$(set +o); + set +x -eu -o pipefail; + + echo "export $1=\"${2:-}\";" >> $BASH_ENV; + + # Restore original shell options. + eval "$originalShellOptions"; +} + + +# Create a function to set an environment variable, when called. +# +# Use this function for creating setter for public environment variables that require expensive or +# time-consuming computaions and may not be needed. When needed, you can call this function to set +# the environment variable (which will be available through `$BASH_ENV` from that point onwards). +# +# Arguments: +# - ``: The name of the environment variable. The generated setter function will be +# `setPublicVar_`. +# - ``: The code to run to compute the value for the variable. Since this code should be +# executed lazily, it must be properly escaped. For example: +# ```sh +# # DO NOT do this: +# createPublicVarSetter MY_VAR "$(whoami)"; # `whoami` will be evaluated eagerly +# +# # DO this isntead: +# createPublicVarSetter MY_VAR "\$(whoami)"; # `whoami` will NOT be evaluated eagerly +# ``` +# +# Usage: `createPublicVarSetter ` +# +# Example: +# ```sh +# createPublicVarSetter MY_VAR 'echo "FOO"'; +# echo $MY_VAR; # Not defined +# +# setPublicVar_MY_VAR; +# source $BASH_ENV; +# echo $MY_VAR; # FOO +# ``` +function createPublicVarSetter() { + echo "setPublicVar_$1() { setPublicVar $1 \"$2\"; }" >> $BASH_ENV; +} diff --git a/.circleci/env.sh b/.circleci/env.sh new file mode 100755 index 000000000000..338371017ccb --- /dev/null +++ b/.circleci/env.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Variables +readonly projectDir=$(realpath "$(dirname ${BASH_SOURCE[0]})/..") +readonly envHelpersPath="$projectDir/.circleci/env-helpers.inc.sh"; + +# Load helpers and make them available everywhere (through `$BASH_ENV`). +source $envHelpersPath; +echo "source $envHelpersPath;" >> $BASH_ENV; + +#################################################################################################### +# Define PUBLIC environment variables for CircleCI. +#################################################################################################### +# See https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables for more info. +#################################################################################################### +setPublicVar CI "$CI" +setPublicVar PROJECT_ROOT "$projectDir"; +# This is the branch being built; e.g. `pull/12345` for PR builds. +setPublicVar CI_BRANCH "$CIRCLE_BRANCH"; +setPublicVar CI_BUILD_URL "$CIRCLE_BUILD_URL"; +setPublicVar CI_COMMIT "$CIRCLE_SHA1"; +setPublicVar CI_GIT_BASE_REVISION "${CIRCLE_GIT_BASE_REVISION}"; +setPublicVar CI_GIT_REVISION "${CIRCLE_GIT_REVISION}"; +setPublicVar CI_GIT_TAG "${CIRCLE_TAG:-false}"; +setPublicVar CI_COMMIT_RANGE "$CIRCLE_GIT_BASE_REVISION..$CIRCLE_GIT_REVISION"; +setPublicVar CI_PULL_REQUEST "${CIRCLE_PR_NUMBER:-false}"; +setPublicVar CI_REPO_NAME "$CIRCLE_PROJECT_REPONAME"; +setPublicVar CI_REPO_OWNER "$CIRCLE_PROJECT_USERNAME"; +setPublicVar CI_PR_REPONAME "$CIRCLE_PR_REPONAME"; +setPublicVar CI_PR_USERNAME "$CIRCLE_PR_USERNAME"; + + +#################################################################################################### +# Define SauceLabs environment variables for CircleCI. +#################################################################################################### +setPublicVar BROWSER_PROVIDER "saucelabs" + +# The currently latest-1 version of desktop Safari on Saucelabs (v12.0) is unstable and disconnects +# consistently. The latest version (v12.1) works fine. +# TODO: Add `SL_Safari-1` back, once it no longer corresponds to v12.0. +setPublicVar BROWSERS "SL_Chrome,SL_Chrome-1,\ +SL_Firefox,SL_Firefox-1,\ +SL_Safari,\ +SL_iOS,SL_iOS-1,\ +SL_IE_9,SL_IE_10,SL_IE_11,\ +SL_EDGE,SL_EDGE-1" + +setPublicVar SAUCE_LOG_FILE /tmp/angular/sauce-connect.log +setPublicVar SAUCE_READY_FILE /tmp/angular/sauce-connect-ready-file.lock +setPublicVar SAUCE_PID_FILE /tmp/angular/sauce-connect-pid-file.lock +setPublicVar SAUCE_TUNNEL_IDENTIFIER "angularjs-framework-${CIRCLE_BUILD_NUM}-${CIRCLE_NODE_INDEX}" +# Amount of seconds we wait for sauceconnect to establish a tunnel instance. In order to not +# acquire CircleCI instances for too long if sauceconnect failed, we need a connect timeout. +setPublicVar SAUCE_READY_FILE_TIMEOUT 120 + +#################################################################################################### +# Define additional environment variables +#################################################################################################### + +# NOTE: Make sure the tools used to compute this are available in all executors in `config.yml`. +setPublicVar DIST_TAG $( cat package.json | grep distTag | sed -E 's/^\s*"distTag"\s*:\s*"([^"]+)"\s*,\s*$/\1/' ) + +#################################################################################################### +#################################################################################################### +## Source `$BASH_ENV` to make the variables available immediately. ## +## *** NOTE: This must remain the last command in this script. *** ## +#################################################################################################### +#################################################################################################### +source $BASH_ENV; diff --git a/.editorconfig b/.editorconfig index f6a54e4dd2c5..a6bc2855214e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# http://editorconfig.org +# https://editorconfig.org root = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000000..6d8222eb45db --- /dev/null +++ b/.eslintignore @@ -0,0 +1,10 @@ +build/** +docs/app/assets/js/angular-bootstrap/** +docs/config/templates/** +node_modules/** +lib/htmlparser/** +src/angular.bind.js +src/ngParseExt/ucd.js +i18n/closure/** +tmp/** +vendor/** diff --git a/.eslintrc-base.json b/.eslintrc-base.json new file mode 100644 index 000000000000..ee3a411bb2d7 --- /dev/null +++ b/.eslintrc-base.json @@ -0,0 +1,117 @@ +{ + "rules": { + // Rules are divided into sections from http://eslint.org/docs/rules/ + + // Possible errors + "comma-dangle": ["error", "never"], + "no-cond-assign": ["error", "except-parens"], + "no-constant-condition": ["error", {"checkLoops": false}], + "no-control-regex": "error", + "no-debugger": "error", + "no-dupe-args": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty-character-class": "error", + "no-empty": "error", + "no-ex-assign": "error", + "no-extra-boolean-cast": "error", + "no-extra-semi": "error", + "no-func-assign": "error", + "no-inner-declarations": "error", + "no-invalid-regexp": "error", + "no-irregular-whitespace": "error", + "no-negated-in-lhs": "error", + "no-obj-calls": "error", + "no-regex-spaces": "error", + "no-sparse-arrays": "error", + "no-unreachable": "error", + "use-isnan": "error", + "no-unsafe-finally": "error", + "valid-typeof": "error", + "no-unexpected-multiline": "error", + + // Best practices + "accessor-pairs": "error", + "array-callback-return": "error", + "eqeqeq": ["error", "allow-null"], + "no-alert": "error", + "no-caller": "error", + "no-case-declarations": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-label": "error", + "no-fallthrough": "error", + "no-floating-decimal": "error", + "no-implied-eval": "error", + "no-invalid-this": "error", + "no-iterator": "error", + "no-multi-str": "error", + "no-new-func": "error", + "no-new-wrappers": "error", + "no-new": "error", + "no-octal-escape": "error", + "no-octal": "error", + "no-proto": "error", + "no-redeclare": "error", + "no-return-assign": "error", + "no-script-url": "error", + "no-self-assign": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-throw-literal": "error", + "no-unmodified-loop-condition": "error", + "no-unused-expressions": "error", + "no-unused-labels": "error", + "no-useless-call": "error", + "no-useless-concat": "error", + "no-useless-escape": "error", + "no-void": "error", + "no-with": "error", + "radix": "error", + "wrap-iife": ["error", "inside"], + + // Strict mode + "strict": ["error", "global"], + + // Variables + "no-delete-var": "error", + "no-label-var": "error", + "no-restricted-globals": ["error", "event"], + "no-shadow-restricted-names": "error", + "no-undef-init": "error", + "no-undef": "error", + "no-unused-vars": ["error", { "vars": "local", "args": "none" }], + + // Node.js + "handle-callback-err": "error", + + // Stylistic issues + "array-bracket-spacing": ["error", "never"], + "brace-style": ["error", "1tbs", { "allowSingleLine": true }], + "comma-style": ["error", "last"], + "eol-last": "error", + "keyword-spacing": "error", + "linebreak-style": ["error", "unix"], + "max-len": ["error", { "code": 200, "ignoreComments": true, "ignoreUrls": true }], + "new-cap": "error", + "new-parens": "error", + "no-array-constructor": "error", + "no-bitwise": "error", + "no-mixed-spaces-and-tabs": "error", + "no-multiple-empty-lines": ["error", { "max": 3, "maxEOF": 1 }], + "no-whitespace-before-property": "error", + "no-spaced-func": "error", + "no-trailing-spaces": "error", + "no-unneeded-ternary": "error", + "quotes": ["error", "single"], + "semi-spacing": "error", + "semi": "error", + "space-before-blocks": ["error", "always"], + "space-before-function-paren": ["error", "never"], + "space-in-parens": ["error", "never"], + "space-infix-ops": "error", + "space-unary-ops": ["error", { "words": true, "nonwords": false }], + "unicode-bom": ["error", "never"] + } +} diff --git a/.eslintrc-browser.json b/.eslintrc-browser.json new file mode 100644 index 000000000000..44024664ae8f --- /dev/null +++ b/.eslintrc-browser.json @@ -0,0 +1,17 @@ +{ + "extends": "./.eslintrc-base.json", + + "env": { + // Note: don't set `"browser": true`; code in "src/" should be compatible with + // non-browser environments like Node.js with a custom window implementation + // like jsdom. All browser globals should be taken from window. + "browser": false, + "node": false + }, + + "globals": { + "window": false, + + "angular": false + } +} diff --git a/.eslintrc-node.json b/.eslintrc-node.json new file mode 100644 index 000000000000..c16a8a883837 --- /dev/null +++ b/.eslintrc-node.json @@ -0,0 +1,13 @@ +{ + "extends": "./.eslintrc-base.json", + "env": { + "browser": false, + "node": true + }, + "parserOptions": { + "ecmaVersion": 2017 + }, + "plugins": [ + "promise" + ] +} diff --git a/.eslintrc-todo.json b/.eslintrc-todo.json new file mode 100644 index 000000000000..a7b24d7a05b0 --- /dev/null +++ b/.eslintrc-todo.json @@ -0,0 +1,25 @@ +{ + // This config contains proposed rules that we'd like to have enabled but haven't + // converted the code to adhere yet. If a decision comes to not enable one of these + // rules, it should be removed from the file. Every rule that got enabled in the + // end should be moved from here to a respective section in .eslintrc.json + + "rules": { + // Rules are divided into sections from http://eslint.org/docs/rules/ + + // Best practices + "complexity": ["error", 10], + "dot-notation": "error", + "dot-location": ["error", "property"], + + // Stylistic issues + "block-spacing": ["error", "always"], + "comma-spacing": "error", + "id-denylist": ["error", "event"], + "indent": ["error", 2], + "key-spacing": ["error", { "beforeColon": false, "afterColon": true, "mode": "minimum" }], + "object-curly-spacing": ["error", "never"], + "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], + "operator-linebreak": ["error", "after", { "overrides": { "?": "before", ":": "before" }}] + } +} diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000000..d8de7a976909 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "root": true, + "extends": "./.eslintrc-node.json" +} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000000..f5513f23390c --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,42 @@ +# AngularJS is in LTS mode +We are no longer accepting changes that are not critical bug fixes into this project. +See https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c for more detail. + + + + + +**I'm submitting a ...** + +- [ ] regression from 1.7.0 +- [ ] security issue +- [ ] issue caused by a new browser version +- [ ] other + +**Current behavior:** + + +**Expected / new behavior:** + + +**Minimal reproduction of the problem with instructions:** + + +**AngularJS version:** 1.8.x + + +**Browser:** [all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView | Opera XX ] + + +**Anything else:** + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000000..fd23b045065a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,29 @@ +# AngularJS is in LTS mode +We are no longer accepting changes that are not critical bug fixes into this project. +See https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c for more detail. + + +**Does this PR fix a regression since 1.7.0, a security flaw, or a problem caused by a new browser version?** + + + + +**What is the current behavior? (You can also link to an open issue here)** + + + +**What is the new behavior (if this is a feature change)?** + + + +**Does this PR introduce a breaking change?** + + + +**Please check if the PR fulfills these requirements** +- [ ] The commit message follows our [guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits) +- [ ] Fix/Feature: [Docs](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#documentation) have been added/updated +- [ ] Fix/Feature: Tests have been added; existing tests pass + +**Other information**: + diff --git a/.gitignore b/.gitignore index dcfa68efd8e1..9641ed4fd609 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,9 @@ performance/temp*.html *~ *.swp angular.js.tmproj -/node_modules/ -bower_components/ +node_modules/ angular.xcodeproj +.firebase/ .idea *.iml .agignore @@ -19,4 +19,9 @@ angular.xcodeproj libpeerconnection.log npm-debug.log /tmp/ -/scripts/bower/bower-* +.vscode +*.log +*.stackdump +scripts/code.angularjs.org-firebase/deploy +scripts/docs.angularjs.org-firebase/deploy +scripts/docs.angularjs.org-firebase/functions/content diff --git a/.jscs.json b/.jscs.json deleted file mode 100644 index 4e0292635483..000000000000 --- a/.jscs.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "excludeFiles": ["src/ngLocale/**"], - "disallowKeywords": ["with"], - "disallowMixedSpacesAndTabs": true, - "disallowMultipleLineStrings": true, - "disallowNewlineBeforeBlockStatements": true, - "disallowSpaceAfterObjectKeys": true, - "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], - "disallowSpaceBeforeBinaryOperators": [","], - "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], - "disallowSpacesInAnonymousFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInFunctionDeclaration": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInNamedFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInsideArrayBrackets": true, - "disallowSpacesInsideParentheses": true, - "disallowTrailingComma": true, - "disallowTrailingWhitespace": true, - "requireCommaBeforeLineBreak": true, - "requireLineFeedAtFileEnd": true, - "requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], - "requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], - "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], - "requireSpaceBeforeBlockStatements": true, - "requireSpacesInConditionalExpression": { - "afterTest": true, - "beforeConsequent": true, - "afterConsequent": true, - "beforeAlternate": true - }, - "requireSpacesInFunction": { - "beforeOpeningCurlyBrace": true - }, - "validateLineBreaks": "LF", - "validateParameterSeparator": ", " -} diff --git a/.jscs.json.todo b/.jscs.json.todo deleted file mode 100644 index 3bc5806e86e0..000000000000 --- a/.jscs.json.todo +++ /dev/null @@ -1,15 +0,0 @@ -// This is an incomplete TODO list of checks we want to start enforcing -// -// The goal is to enable these checks one by one by moving them to .jscs.json along with commits -// that correct the existing code base issues and make the new check pass. - -{ - "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"], - "disallowImplicitTypeConversion": ["string"], - "disallowMultipleLineBreaks": true, - "disallowKeywordsOnNewLine": ["else"], - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": true - } -} diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index e9cc4f260316..000000000000 --- a/.jshintignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules/** -lib/htmlparser/** diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 7fbaafbc0a8c..000000000000 --- a/.jshintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": ".jshintrc-base", - "node": true, - "globals": {} -} diff --git a/.jshintrc-base b/.jshintrc-base deleted file mode 100644 index c4ac5e2666bf..000000000000 --- a/.jshintrc-base +++ /dev/null @@ -1,19 +0,0 @@ -{ - "bitwise": true, - "immed": true, - "newcap": true, - "noarg": true, - "noempty": true, - "nonew": true, - "trailing": true, - "maxlen": 200, - "boss": true, - "eqnull": true, - "expr": true, - "globalstrict": true, - "laxbreak": true, - "loopfunc": true, - "sub": true, - "undef": true, - "indent": 2 -} diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000000..f1a2dc0b18e0 --- /dev/null +++ b/.mailmap @@ -0,0 +1,29 @@ +Andres Ornelas +Caitlin Potter +Caitlin Potter +Di Peng +Di Peng +Georgios Kalpakas +Georgios Kalpakas +Julie Ralph +Lucas Galfaso +Martin Staffa +Martin Staffa +Matias Niemelä +Michał Gołębiowski-Owczarek +Misko Hevery +Misko Hevery +Igor Minar +Igor Minar +Igor Minar +Igor Minar +Pawel Kozlowski +Peter Bacon Darwin +Rodric Haddad +Shahar Talmi +Shahar Talmi +Shyam Seshadri +Shyam Seshadri +Vojta Jina +Vojta Jina +Vojta Jina diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000000..6b17d228d335 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +14.16.1 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 49139f994228..000000000000 --- a/.travis.yml +++ /dev/null @@ -1,59 +0,0 @@ -language: node_js -node_js: - - '0.10' - -branches: - except: - - /^g3_.*$/ - -env: - matrix: - - JOB=unit BROWSER_PROVIDER=saucelabs - - JOB=docs-e2e BROWSER_PROVIDER=saucelabs - - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs - - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs - - JOB=unit BROWSER_PROVIDER=browserstack - - JOB=docs-e2e BROWSER_PROVIDER=browserstack - - JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack - - JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack - global: - - SAUCE_USERNAME=angular-ci - - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987 - - BROWSER_STACK_USERNAME=VojtaJina - - BROWSER_STACK_ACCESS_KEY=QCQJ1ZpWXpBkSwEdD8ev - - LOGS_DIR=/tmp/angular-build/logs - - BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready - -matrix: - allow_failures: - - env: "JOB=unit BROWSER_PROVIDER=browserstack" - -install: - # - npm config set registry http://23.251.144.68 - # Disable the spinner, it looks bad on Travis - - npm config set spin false - # Log HTTP requests - - npm config set loglevel http - - time ./scripts/travis/npm-bundle-deps.sh - - time npm install - -before_script: - - mkdir -p $LOGS_DIR - - ./scripts/travis/start_browser_provider.sh - - npm install -g grunt-cli - - grunt package - - ./scripts/travis/wait_for_browser_provider.sh - -script: - - ./scripts/travis/build.sh - -after_script: - - ./scripts/travis/print_logs.sh - -notifications: - webhooks: - urls: - - https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2 - on_success: change # options: [always|never|change] default: always - on_failure: always # options: [always|never|change] default: always - on_start: false # default: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e9144b7587..c720bd43ffa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9265 @@ +**AngularJS support has officially ended as of January 2022. +[See what ending support means](https://docs.angularjs.org/misc/version-support-status) +and [read the end of life announcement](https://goo.gle/angularjs-end-of-life).** + +**Visit [angular.io](https://angular.io) for the actively supported Angular.** + + +# 1.8.3 ultimate-farewell (2022-04-07) + +One final release of AngularJS in order to update package README files on npm. + + +# 1.8.2 meteoric-mining (2020-10-21) + +## Bug Fixes +- **$sceDelegate:** ensure that `resourceUrlWhitelist()` is identical to `trustedResourceUrlList()` + ([e41f01](https://github.com/angular/angular.js/commit/e41f018959934bfbf982ba996cd654b1fce88d43), + [#17090](https://github.com/angular/angular.js/issues/17090)) + + + +# 1.8.1 mutually-supporting (2020-09-30) + +## Bug Fixes +- **$sanitize:** do not trigger CSP alert/report in Firefox and Chrome + ([2fab3d](https://github.com/angular/angular.js/commit/2fab3d4e00f4fe35bfa3cf255160cb97404baf24)) + +## Refactorings + +- **SanitizeUriProvider:** remove usages of whitelist + ([76738102](https://github.com/angular/angular.js/commit/767381020d88bda2855ac87ca6f00748907e14ff)) +- **httpProvider:** remove usages of whitelist and blacklist + ([c953af6b](https://github.com/angular/angular.js/commit/c953af6b8cfeefe4acc0ca358550eed5da8cfe00)) +- **sceDelegateProvider:** remove usages of whitelist and blacklist + ([a206e267](https://github.com/angular/angular.js/commit/a206e2675c351c3cdcde3402978126774c1c5df9)) + +## Deprecation Notices + +- Deprecated ~~`$compileProvider.aHrefSanitizationWhitelist`~~. + It is now [`aHrefSanitizationTrustedUrlList`](https://docs.angularjs.org/api/ng/provider/$compileProvider#aHrefSanitizationTrustedUrlList). +- Deprecated ~~`$compileProvider.imgSrcSanitizationWhitelist`~~. + It is now [`imgSrcSanitizationTrustedUrlList`](https://docs.angularjs.org/api/ng/provider/$compileProvider#imgSrcSanitizationTrustedUrlList). +- Deprecated ~~`$httpProvider.xsrfWhitelistedOrigins`~~. + It is now [`xsrfTrustedOrigins`](https://docs.angularjs.org/api/ng/provider/$httpProvider#xsrfTrustedOrigins). +- Deprecated ~~`$sceDelegateProvider.resourceUrlWhitelist`~~. + It is now [`trustedResourceUrlList`](https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#trustedResourceUrlList). +- Deprecated ~~`$sceDelegateProvider.resourceUrlBlacklist`~~. + It is now [`bannedResourceUrlList`](https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider#bannedResourceUrlList). + +For the purposes of backward compatibility, the previous symbols are aliased to their new symbol. + + + +# 1.8.0 nested-vaccination (2020-06-01) + +_This release contains a breaking change to resolve a security issue which was discovered by +Krzysztof Kotowicz(@koto); and independently by Esben Sparre Andreasen (@esbena) while +performing a Variant Analysis of [CVE-2020-11022](https://github.com/advisories/GHSA-gxr4-xjj5-5px2) +which itself was found and reported by Masato Kinugawa (@masatokinugawa)._ + +## Bug Fixes +- **jqLite:** + - prevent possible XSS due to regex-based HTML replacement + ([2df43c](https://github.com/angular/angular.js/commit/2df43c07779137d1bddf7f3b282a1287a8634acd)) + +## Breaking Changes + +### **jqLite** due to: + - **[2df43c](https://github.com/angular/angular.js/commit/2df43c07779137d1bddf7f3b282a1287a8634acd)**: prevent possible XSS due to regex-based HTML replacement + +JqLite no longer turns XHTML-like strings like `
` to sibling elements `
` +when not in XHTML mode. Instead it will leave them as-is. The browser, in non-XHTML mode, will convert these to: +`
`. + +This is a security fix to avoid an XSS vulnerability if a new jqLite element is created from a user-controlled HTML string. +If you must have this functionality and understand the risk involved then it is posible to restore the original behavior by calling + +```js +angular.UNSAFE_restoreLegacyJqLiteXHTMLReplacement(); +``` + +But you should adjust your code for this change and remove your use of this function as soon as possible. + +Note that this only patches jqLite. If you use jQuery 3.5.0 or newer, please read the [jQuery 3.5 upgrade guide](https://jquery.com/upgrade-guide/3.5/) for more details about the workarounds. + + + +# 1.7.9 pollution-eradication (2019-11-19) + +## Bug Fixes +- **angular.merge:** do not merge __proto__ property + ([726f49](https://github.com/angular/angular.js/commit/726f49dcf6c23106ddaf5cfd5e2e592841db743a)) +
(Thanks to the [Snyk Security Research Team](https://snyk.io/blog/snyk-research-team-discovers-severe-prototype-pollution-security-vulnerabilities-affecting-all-versions-of-lodash/) for identifyng this issue.) +- **ngStyle:** correctly remove old style when new style value is invalid + ([5edd25](https://github.com/angular/angular.js/commit/5edd25364f617083363dc2bd61f9230b38267578), + [#16860](https://github.com/angular/angular.js/issues/16860), + [#16868](https://github.com/angular/angular.js/issues/16868)) + + + +# 1.7.8 enthusiastic-oblation (2019-03-11) + + +## Bug Fixes +- **required:** correctly validate required on non-input element surrounded by ngIf + ([a4c7bd](https://github.com/angular/angular.js/commit/a4c7bdccd76c39c30e33f6215da9a00cc8acde2c), + [#16830](https://github.com/angular/angular.js/issues/16830), + [#16836](https://github.com/angular/angular.js/issues/16836)) + + + +# 1.7.7 kingly-exiting (2019-02-04) + +## Bug Fixes +- **ngRequired:** set error correctly when inside ngRepeat and false by default + ([5ad4f5](https://github.com/angular/angular.js/commit/5ad4f5562c37b1cb575e3e5fddd96e9dd10408e2), + [#16814](https://github.com/angular/angular.js/issues/16814), + [#16820](https://github.com/angular/angular.js/issues/16820)) + + + +# 1.7.6 gravity-manipulation (2019-01-17) + +## Bug Fixes +- **$compile:** fix ng-prop-* with undefined values + ([772440](https://github.com/angular/angular.js/commit/772440cdaf9a9bfa40de1675e20a5f0e356089ed), + [#16797](https://github.com/angular/angular.js/issues/16797), + [#16798](https://github.com/angular/angular.js/issues/16798)) +- **compile:** properly handle false value for boolean attrs with jQuery + ([27486b](https://github.com/angular/angular.js/commit/27486bd15e70946ece2ba713e4e8654b7f9bddad), + [#16778](https://github.com/angular/angular.js/issues/16778), + [#16779](https://github.com/angular/angular.js/issues/16779)) +- **ngRepeat:** + - fix reference to last collection value remaining across linkages + ([cf919a](https://github.com/angular/angular.js/commit/cf919a6fb7fc655f3fa37a74899a797ea5b8073e)) + - fix trackBy function being invoked with incorrect scope + ([d4d103](https://github.com/angular/angular.js/commit/d4d1031bcd9b30ae6a58bd60a79bcc9d20f0f2b7), + [#16776](https://github.com/angular/angular.js/issues/16776), + [#16777](https://github.com/angular/angular.js/issues/16777)) +- **aria/ngClick:** check if element is `contenteditable` before blocking spacebar + ([289374](https://github.com/angular/angular.js/commit/289374a43c1b2fd715ddf7455db225b17afebbaf), + [#16762](https://github.com/angular/angular.js/issues/16762)) +- **input:** prevent browsers from autofilling hidden inputs + ([7cbb10](https://github.com/angular/angular.js/commit/7cbb1044fcb3576cdad791bd22ebea3dfd533ff8)) +- **Angular:** add workaround for Safari / Webdriver problem + ([eb49f6](https://github.com/angular/angular.js/commit/eb49f6b7555cfd7ab03fd35581adb6b4bd49044e)) +- **$browser:** normalize inputted URLs + ([2f72a6](https://github.com/angular/angular.js/commit/2f72a69ded53a122afad3ec28d91f9bd2f41eb4f), + [#16606](https://github.com/angular/angular.js/issues/16606)) +- **interpolate:** do not create directives for constant media URL attributes + ([90a41d](https://github.com/angular/angular.js/commit/90a41d415c83abdbf28317f49df0fd0a7e07db86), + [#16734](https://github.com/angular/angular.js/issues/16734)) +- **$q:** allow third-party promise libraries + ([eefaa7](https://github.com/angular/angular.js/commit/eefaa76a90dbef08fdc7d734a205cc2de50d9f91), + [#16164](https://github.com/angular/angular.js/issues/16164), + [#16471](https://github.com/angular/angular.js/issues/16471)) +- **urlUtils:** make IPv6 URL's hostname wrapped in square brackets in IE/Edge + ([0e1bd7](https://github.com/angular/angular.js/commit/0e1bd7822e61822a48b8fd7ba5913a8702e6dabf), + [#16692](https://github.com/angular/angular.js/issues/16692), + [#16715](https://github.com/angular/angular.js/issues/16715)) +- **ngAnimateSwap:** make it compatible with `ngIf` on the same element + ([b27080](https://github.com/angular/angular.js/commit/b27080d52546409fb4e483f212f03616e2ca8037), + [#16616](https://github.com/angular/angular.js/issues/16616), + [#16729](https://github.com/angular/angular.js/issues/16729)) +- **ngMock:** make matchLatestDefinitionEnabled work + ([3cdffc](https://github.com/angular/angular.js/commit/3cdffcecbae71189b4db69b57fadda6608a23b61), + [#16702](https://github.com/angular/angular.js/issues/16702)) +- **ngStyle:** skip setting empty value when new style has the property + ([d6098e](https://github.com/angular/angular.js/commit/d6098eeb1c9510d599e9bd3cfdba7dd21e7a55a5), + [#16709](https://github.com/angular/angular.js/issues/16709)) + +## Performance Improvements +- **input:** prevent multiple validations on initialization + ([692622](https://github.com/angular/angular.js/commit/69262239632027b373258e75c670b89132ad9edb), + [#14691](https://github.com/angular/angular.js/issues/14691), + [#16760](https://github.com/angular/angular.js/issues/16760)) + + + + +# 1.7.5 anti-prettification (2018-10-04) + +## Bug Fixes +- **ngClass:** do not break on invalid values + ([f3a565](https://github.com/angular/angular.js/commit/f3a565872d802c94bb213944791b11b483d52f73), + [#16697](https://github.com/angular/angular.js/issues/16697), + [#16699](https://github.com/angular/angular.js/issues/16699)) + + + +# 1.7.4 interstellar-exploration (2018-09-07) + +## Bug Fixes +- **ngAria.ngClick:** prevent default event on space/enter only for non-interactive elements + ([61b335](https://github.com/angular/angular.js/commit/61b33543ff8e7f32464dec98a46bf0a35e9b03a4), + [#16664](https://github.com/angular/angular.js/issues/16664), + [#16680](https://github.com/angular/angular.js/issues/16680)) +- **ngAnimate:** remove the "prepare" classes with multiple structural animations + ([3105b2](https://github.com/angular/angular.js/commit/3105b2c26a71594c4e7904efc18f4b2e9da25b1b), + [#16681](https://github.com/angular/angular.js/issues/16681), + [#16677](https://github.com/angular/angular.js/issues/16677)) +- **$route:** correctly extract path params if the path contains a question mark or a hash + ([2ceeb7](https://github.com/angular/angular.js/commit/2ceeb739f35e01fcebcabac4beeeb7684ae9f86d)) +- **ngHref:** allow numbers and other objects in interpolation + ([30084c](https://github.com/angular/angular.js/commit/30084c13699c814ff6703d7aa2d3947a9b2f7067), + [#16652](https://github.com/angular/angular.js/issues/16652), + [#16626](https://github.com/angular/angular.js/issues/16626)) +- **select:** allow to select first option with value `undefined` + ([668a33](https://github.com/angular/angular.js/commit/668a33da3439f17e61dfa8f6d9b114ebde8c9d87), + [#16653](https://github.com/angular/angular.js/issues/16653), + [#16656](https://github.com/angular/angular.js/issues/16656)) + + + +# 1.7.3 eventful-proposal (2018-08-03) + +## Bug Fixes +- **$location:** + - fix infinite recursion/digest on URLs with special characters + ([e68697](https://github.com/angular/angular.js/commit/e68697e2e30695f509e6c2c1e43c2c02b7af41f0), + [#16592](https://github.com/angular/angular.js/issues/16592), + [#16611](https://github.com/angular/angular.js/issues/16611)) + - avoid unnecessary `$locationChange*` events due to empty hash + ([1144b1](https://github.com/angular/angular.js/commit/1144b1eccb886ea0e4a80bcb07d38a305c3263b4), + [#16632](https://github.com/angular/angular.js/issues/16632), + [#16636](https://github.com/angular/angular.js/issues/16636)) +- **ngMock.$httpBackend:** + - pass failed HTTP expectations to `$exceptionHandler` + ([4adbf8](https://github.com/angular/angular.js/commit/4adbf82a84a564a8d3f0982c17a64c6163200bcd), + [#16644](https://github.com/angular/angular.js/issues/16644)) + - correctly ignore query params in {expect,when}Route + ([be417f](https://github.com/angular/angular.js/commit/be417f28549e184fbc3c7f74251ac21fca965ae8), + [#14173](https://github.com/angular/angular.js/issues/14173), + [#16589](https://github.com/angular/angular.js/issues/16589)) +- **Angular:** add workaround for Safari / Webdriver problem + ([0a1db2](https://github.com/angular/angular.js/commit/0a1db2ad5f8da6902b1711a738ae4177ce9685fa), + [#16645](https://github.com/angular/angular.js/issues/16645)) +- **$animate:** avoid memory leak with `$animate.enabled(element, enabled)` + ([4bd424](https://github.com/angular/angular.js/commit/4bd424690612885ca06028e9b27de585edc3d3c3), + [#16649](https://github.com/angular/angular.js/issues/16649)) +- **$compile:** + - use correct parent element when requiring on html element + ([05ac70](https://github.com/angular/angular.js/commit/05ac702bc7edae5f89c363ea661774910735ea8b), + [#16535](https://github.com/angular/angular.js/issues/16535), + [#16647](https://github.com/angular/angular.js/issues/16647)) + - work around Firefox `DocumentFragment` bug + ([10973c](https://github.com/angular/angular.js/commit/10973c3366676ac8e5b2728b1e006cdef4ea197e), + [#16607](https://github.com/angular/angular.js/issues/16607), + [#16615](https://github.com/angular/angular.js/issues/16615)) +- **ngEventDirs:** + - pass error in handler to $exceptionHandler when event was triggered in a digest + ([688211](https://github.com/angular/angular.js/commit/6882113bc194fb10081db9bab3dd7d69dd59f311)) + - don't wrap the event handler in $apply if already in $digest + ([535ee3](https://github.com/angular/angular.js/commit/535ee32a0b4881c9fd526fb5e0ffc10919ba1800), + [#14673](https://github.com/angular/angular.js/issues/14673), + [#14674](https://github.com/angular/angular.js/issues/14674)) +- **angular.element:** do not break on `cleanData()` if `_data()` returns undefined + ([7cf4a2](https://github.com/angular/angular.js/commit/7cf4a2933cb017e45b0c97b0a836cbbd905ee31a), + [#16641](https://github.com/angular/angular.js/issues/16641), + [#16642](https://github.com/angular/angular.js/issues/16642)) +- **ngAria:** do not scroll when pressing spacebar on custom buttons + ([3a517c](https://github.com/angular/angular.js/commit/3a517c25f677294a7a9eca1660654a3edcc9e103), + [#14665](https://github.com/angular/angular.js/issues/14665), + [#16604](https://github.com/angular/angular.js/issues/16604)) + + +## New Features +- **$compile:** add support for arbitrary DOM property and event bindings + ([a5914c](https://github.com/angular/angular.js/commit/a5914c94a8fa5b1eceeab9e4e6849cbf467bc26d), + [#16428](https://github.com/angular/angular.js/issues/16428), + [#16235](https://github.com/angular/angular.js/issues/16235), + [#16614](https://github.com/angular/angular.js/issues/16614)) +- **ngMock:** add `$flushPendingTasks()` and `$verifyNoPendingTasks()` + ([6f7674](https://github.com/angular/angular.js/commit/6f7674a7d063d434205f75f5b861f167e8125999), + [#14336](https://github.com/angular/angular.js/issues/14336)) +- **core:** implement more granular pending task tracking + ([17b139](https://github.com/angular/angular.js/commit/17b139f107e5471a9351af638093a8e13a69e42a)) +- **$animate:** add option data to event callbacks + ([fc64e6](https://github.com/angular/angular.js/commit/fc64e6807642512b567deb52b497bd2bff570a1f), + [#12697](https://github.com/angular/angular.js/issues/12697), + [#13059](https://github.com/angular/angular.js/issues/13059)) +- **form.FormController:** add $getControls() + ([c9d1e6](https://github.com/angular/angular.js/commit/c9d1e690aa597283373b78e646676fa8f1ba1b4d), + [#16601](https://github.com/angular/angular.js/issues/16601), + [#14749](https://github.com/angular/angular.js/issues/14749), + [#14517](https://github.com/angular/angular.js/issues/14517), + [#13202](https://github.com/angular/angular.js/issues/13202)) +- **ngModelOptions:** add `timeStripZeroSeconds` and `timeSecondsFormat` + ([b68221](https://github.com/angular/angular.js/commit/b682213d72d65c996a6a31ea57b79d4c4f4e3c98), + [#10721](https://github.com/angular/angular.js/issues/10721), + [#16510](https://github.com/angular/angular.js/issues/16510), + [#16584](https://github.com/angular/angular.js/issues/16584)) + + +## Performance Improvements +- **ngAnimate:** avoid repeated calls to addClass/removeClass when animation has no duration + ([093635](https://github.com/angular/angular.js/commit/0936353e9a03f072bc3c4056888fd154a96530ef), + [#14165](https://github.com/angular/angular.js/issues/14165), + [#14166](https://github.com/angular/angular.js/issues/14166), + [#16613](https://github.com/angular/angular.js/issues/16613)) + + + +# 1.7.2 extreme-compatiplication (2018-06-12) + +In the previous release, we removed a private, undocumented API that was no longer used by +AngularJS. It turned out that several popular UI libraries (such as +[AngularJS Material](https://material.angularjs.org/), +[UI Bootstrap](https://angular-ui.github.io/bootstrap/), +[ngDialog](http://likeastore.github.io/ngDialog/) and probably others) relied on that API. + +In order to avoid unnecessary pain for developers, this release reverts the removal of the private +API and restores compatibility of the aforementioned libraries with the latest AngularJS. + +## Reverts +- **$compile:** remove `preAssignBindingsEnabled` leftovers + ([2da495](https://github.com/angular/angular.js/commit/2da49504065e9e2b71a7a5622e45118d8abbe87e), + [#16580](https://github.com/angular/angular.js/pull/16580), + [a81232](https://github.com/angular/angular.js/commit/a812327acda8bc890a4c4e809f0debb761c29625), + [#16595](https://github.com/angular/angular.js/pull/16595)) + + + +# 1.7.1 momentum-defiance (2018-06-08) + + +## Bug Fixes +- **$compile:** support transcluding multi-element directives + ([789db8](https://github.com/angular/angular.js/commit/789db83a8ae0e2db5db13289b2c29e56093d967a), + [#15554](https://github.com/angular/angular.js/issues/15554), + [#15555](https://github.com/angular/angular.js/issues/15555)) +- **ngModel:** do not throw if view value changes on destroyed scope + ([2b6c98](https://github.com/angular/angular.js/commit/2b6c9867369fd3ef1ddb687af1153478ab62ee1b), + [#16583](https://github.com/angular/angular.js/issues/16583), + [#16585](https://github.com/angular/angular.js/issues/16585)) + + +## New Features +- **$compile:** add one-way collection bindings + ([f9d1ca](https://github.com/angular/angular.js/commit/f9d1ca20c38f065f15769fbe23aee5314cb58bd4), + [#14039](https://github.com/angular/angular.js/issues/14039), + [#16553](https://github.com/angular/angular.js/issues/16553), + [#15874](https://github.com/angular/angular.js/issues/15874)) +- **ngRef:** add directive to publish controller, or element into scope + ([bf841d](https://github.com/angular/angular.js/commit/bf841d35120bf3c4655fde46af4105c85a0f1cdc), + [#16511](https://github.com/angular/angular.js/issues/16511)) +- **errorHandlingConfig:** add option to exclude error params from url + ([3d6c45](https://github.com/angular/angular.js/commit/3d6c45d76e30b1b3c4eb9672cf4a93e5251c06b3), + [#14744](https://github.com/angular/angular.js/issues/14744), + [#15707](https://github.com/angular/angular.js/issues/15707), + [#16283](https://github.com/angular/angular.js/issues/16283), + [#16299](https://github.com/angular/angular.js/issues/16299), + [#16591](https://github.com/angular/angular.js/issues/16591)) +- **ngAria:** add support for ignoring a specific element + ([7d9d38](https://github.com/angular/angular.js/commit/7d9d387195292cb5e04984602b752d31853cfea6), + [#14602](https://github.com/angular/angular.js/issues/14602), + [#14672](https://github.com/angular/angular.js/issues/14672), + [#14833](https://github.com/angular/angular.js/issues/14833)) +- **ngCookies:** support samesite option + ([10a229](https://github.com/angular/angular.js/commit/10a229ce1befdeaf6295d1635dc11391c252a91a), + [#16543](https://github.com/angular/angular.js/issues/16543), + [#16544](https://github.com/angular/angular.js/issues/16544)) +- **ngMessages:** add support for default message + ([a8c263](https://github.com/angular/angular.js/commit/a8c263c1947cc85ee60b4732f7e4bcdc7ba463e8), + [#12008](https://github.com/angular/angular.js/issues/12008), + [#12213](https://github.com/angular/angular.js/issues/12213), + [#16587](https://github.com/angular/angular.js/issues/16587)) +- **ngMock, ngMockE2E:** add option to match latest definition for `$httpBackend` request + ([773f39](https://github.com/angular/angular.js/commit/773f39c9345479f5f8b6321236ce6ad96f77aa92), + [#16251](https://github.com/angular/angular.js/issues/16251), + [#11637](https://github.com/angular/angular.js/issues/11637), + [#16560](https://github.com/angular/angular.js/issues/16560)) +- **$route:** add support for the `reloadOnUrl` configuration option + ([f4f571](https://github.com/angular/angular.js/commit/f4f571efdf86d6acbcd5c6b1de66b4b33a259125), + [#7925](https://github.com/angular/angular.js/issues/7925), + [#15002](https://github.com/angular/angular.js/issues/15002)) + + + +# 1.7.0 nonexistent-physiology (2018-05-11) + +**Here are the full changes for the release of 1.7.0 that are not already released in the 1.6.x branch, +which includes commits from 1.7.0-rc.0 and commits from 1.7.0 directly.** + +1.7.0 is the last scheduled release of AngularJS that includes breaking changes. 1.7.x patch +releases will continue to receive bug fixes and non-breaking features until AngularJS enters Long +Term Support mode (LTS) on July 1st 2018. + +## Bug Fixes +- **input:** + - listen on "change" instead of "click" for radio/checkbox ngModels + ([656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e), + [#4516](https://github.com/angular/angular.js/issues/4516), + [#14667](https://github.com/angular/angular.js/issues/14667), + [#14685](https://github.com/angular/angular.js/issues/14685)) +- **input\[number\]:** validate min/max against viewValue + ([aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec), + [#12761](https://github.com/angular/angular.js/issues/12761), + [#16325](https://github.com/angular/angular.js/issues/16325)) +- **input\[date\]:** correctly parse 2-digit years + ([627180](https://github.com/angular/angular.js/commit/627180fb71b92048d5b9ca2606b9eff1fd99387e), + [#16537](https://github.com/angular/angular.js/issues/16537), + [#16539](https://github.com/angular/angular.js/issues/16539)) +- **jqLite:** make removeData() not remove event handlers + ([b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a), + [#15869](https://github.com/angular/angular.js/issues/15869), + [#16512](https://github.com/angular/angular.js/issues/16512)) +- **$compile:** + - remove the preAssignBindingsEnabled flag + ([38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb), + [#15782](https://github.com/angular/angular.js/issues/15782)) + - add `base[href]` to the list of RESOURCE_URL context attributes + ([1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e), + [#15597](https://github.com/angular/angular.js/issues/15597)) +- **$interval:** throw when trying to cancel non-$interval promise + ([a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$timeout:** throw when trying to cancel non-$timeout promise + ([336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$cookies:** remove the deprecated $cookieStore factory + ([73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77), + [#16465](https://github.com/angular/angular.js/issues/16465)) +- **$resource:** fix interceptors and success/error callbacks + ([ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd), + [#6731](https://github.com/angular/angular.js/issues/6731), + [#9334](https://github.com/angular/angular.js/issues/9334), + [#6865](https://github.com/angular/angular.js/issues/6865), + [#16446](https://github.com/angular/angular.js/issues/16446)) +- **$templateRequest:** + - give tpload error the correct namespace + ([c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)) + - always return the template that is stored in the cache + ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e), + [#16225](https://github.com/angular/angular.js/issues/16225)) +- **$animate:** let cancel() reject the runner promise + ([16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83), + [#14204](https://github.com/angular/angular.js/issues/14204), + [#16373](https://github.com/angular/angular.js/issues/16373)) +- **ngTouch:** + - deprecate the module and its contents + ([67f54b](https://github.com/angular/angular.js/commit/67f54b660038de2b4346b3e76d66a8dc8ccb1f9b), + [#16427](https://github.com/angular/angular.js/issues/16427), + [#16431](https://github.com/angular/angular.js/issues/16431)) + - remove ngClick override, `$touchProvider`, and `$touch` + ([11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50), + [#15761](https://github.com/angular/angular.js/issues/15761), + [#15755](https://github.com/angular/angular.js/issues/15755)) +- **ngScenario:** completely remove the angular scenario runner + ([0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60), + [#9405](https://github.com/angular/angular.js/issues/9405)) +- **form:** set $submitted to true on child forms when parent is submitted + ([223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77), + [#10071](https://github.com/angular/angular.js/issues/10071)) +- **$rootScope:** + - provide correct value of one-time bindings in watchGroup + ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)) + - don't allow explicit digest calls to affect $evalAsync + ([02c046](https://github.com/angular/angular.js/commit/02c04690da16a9bef55694f5db0b8368dc0125c9), + [#15127](https://github.com/angular/angular.js/issues/15127), + [#15494](https://github.com/angular/angular.js/issues/15494)) +- **ngAria:** do not set aria attributes on input[type="hidden"] + ([6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b), + [#15113](https://github.com/angular/angular.js/issues/15113), + [#16367](https://github.com/angular/angular.js/issues/16367)) +- **ngModel, input:** improve handling of built-in named parsers + ([74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140), + [#14292](https://github.com/angular/angular.js/issues/14292), + [#10076](https://github.com/angular/angular.js/issues/10076), + [#16347](https://github.com/angular/angular.js/issues/16347)) +- **$httpParamSerializerJQLike:** + - call functions as jQuery does + ([a784fa](https://github.com/angular/angular.js/commit/a784fab605d825f1158c6292b3c42f8c4a502fdf), + [#16138](https://github.com/angular/angular.js/issues/16138), + [#16139](https://github.com/angular/angular.js/issues/16139)) + - follow jQuery for `null` and `undefined` + ([301fdd](https://github.com/angular/angular.js/commit/301fdda648680d89ccab607c413a7ddede7b0165)) +- **$parse:** + - do not pass scope/locals to interceptors of one-time bindings + ([87a586](https://github.com/angular/angular.js/commit/87a586eb9a23cfd0d0bb681cc778b4b8e5c8451d)) + - always pass the intercepted value to watchers + ([2ee503](https://github.com/angular/angular.js/commit/2ee5033967d5f87a516bad137686b0592e25d26b), + [#16021](https://github.com/angular/angular.js/issues/16021)) + - respect the interceptor.$stateful flag + ([de7403](https://github.com/angular/angular.js/commit/de74034ddf6f92505ccdb61be413a6df2c723f87)) +- **Angular:** remove `angular.lowercase` and `angular.uppercase` + ([1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de), + [#15445](https://github.com/angular/angular.js/issues/15445)) +- **$controller:** remove instantiating controllers defined on window + ([e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16), + [#15349](https://github.com/angular/angular.js/issues/15349), + [#15762](https://github.com/angular/angular.js/issues/15762)) + + +## New Features +- **angular.isArray:** support Array subclasses in `angular.isArray()` + ([e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948), + [#15533](https://github.com/angular/angular.js/issues/15533), + [#15541](https://github.com/angular/angular.js/issues/15541)) +- **$sce:** handle URL sanitization through the `$sce` service + ([1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)) +- **orderBy:** consider `null` and `undefined` greater than other values + ([1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8), + [#15294](https://github.com/angular/angular.js/issues/15294), + [#16376](https://github.com/angular/angular.js/issues/16376)) +- **$resource:** add support for `request` and `requestError` interceptors (#15674) + ([240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded), + [#5146](https://github.com/angular/angular.js/issues/5146)) +- **ngModelOptions:** add debounce catch-all + allow debouncing 'default' only + ([55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68), + [#15411](https://github.com/angular/angular.js/issues/15411), + [#16335](https://github.com/angular/angular.js/issues/16335)) +- **$compile:** lower the `xlink:href` security context for SVG's `a` and `image` elements + ([6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd), + [#15736](https://github.com/angular/angular.js/issues/15736)) + + +## Performance Improvements +- **$rootScope:** allow $watchCollection use of expression input watching + ([97b00c](https://github.com/angular/angular.js/commit/97b00ca497676aaff8a803762a9f8c7ff4aa24dd)) +- **ngStyle:** use $watchCollection + ([15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0), + [#15947](https://github.com/angular/angular.js/issues/15947)) +- **$compile:** do not use deepWatch in literal one-way bindings + ([fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727), + [#15301](https://github.com/angular/angular.js/issues/15301)) + + + + +## Breaking Changes + +### **jqLite** due to: + - **[b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**: make removeData() not remove event handlers + +Before this commit `removeData()` invoked on an element removed its event +handlers as well. If you want to trigger a full cleanup of an element, change: + +```js +elem.removeData(); +``` + +to: + +```js +angular.element.cleanData(elem); +``` + +In most cases, though, cleaning up after an element is supposed to be done +only when it's removed from the DOM as well; in such cases the following: + +```js +elem.remove(); +``` + +will remove event handlers as well. + +### **$cookies** due to: + - **[73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**: remove the deprecated $cookieStore factory + +The $cookieStore has been removed. Migrate to the $cookies service. Note that +for object values you need to use the `putObject` & `getObject` methods as +`get`/`put` will not correctly save/retrieve them. + +Before: +```js +$cookieStore.put('name', {key: 'value'}); +$cookieStore.get('name'); // {key: 'value'} +$cookieStore.remove('name'); +``` + +After: +```js +$cookies.putObject('name', {key: 'value'}); +$cookies.getObject('name'); // {key: 'value'} +$cookies.remove('name'); +``` + +### **$resource** due to: + - **[ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**: fix interceptors and success/error callbacks + +If you are not using `success` or `error` callbacks with `$resource`, +your app should not be affected by this change. + +If you are using `success` or `error` callbacks (with or without +response interceptors), one (subtle) difference is that throwing an +error inside the callbacks will not propagate to the returned +`$promise`. Therefore, you should try to use the promises whenever +possible. E.g.: + +```js +// Avoid +User.query(function onSuccess(users) { throw new Error(); }). + $promise. + catch(function onError() { /* Will not be called. */ }); + +// Prefer +User.query(). + $promise. + then(function onSuccess(users) { throw new Error(); }). + catch(function onError() { /* Will be called. */ }); +``` + +Finally, if you are using `success` or `error` callbacks with response +interceptors, the callbacks will now always run _after_ the interceptors +(and wait for them to resolve in case they return a promise). +Previously, the `error` callback was called before the `responseError` +interceptor and the `success` callback was synchronously called after +the `response` interceptor. E.g.: + +```js +var User = $resource('/api/users/:id', {id: '@id'}, { + get: { + method: 'get', + interceptor: { + response: function(response) { + console.log('responseInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseInterceptor-2'); + return response.resource; + }); + }, + responseError: function(response) { + console.log('responseErrorInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseErrorInterceptor-2'); + return $q.reject('Ooops!'); + }); + } + } + } +}); +var onSuccess = function(value) { console.log('successCallback', value); }; +var onError = function(error) { console.log('errorCallback', error); }; + +// Assuming the following call is successful... +User.get({id: 1}, onSuccess, onError); + // Old behavior: + // responseInterceptor-1 + // successCallback, {/* Promise object */} + // responseInterceptor-2 + // New behavior: + // responseInterceptor-1 + // responseInterceptor-2 + // successCallback, {/* User object */} + +// Assuming the following call returns an error... +User.get({id: 2}, onSuccess, onError); + // Old behavior: + // errorCallback, {/* Response object */} + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // New behavior: + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // errorCallback, Ooops! +``` + + - **[240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**: add support for `request` and `requestError` interceptors (#15674) + +Previously, calling a `$resource` method would synchronously call +`$http`. Now, it will be called asynchronously (regardless if a +`request`/`requestError` interceptor has been defined. + +This is not expected to affect applications at runtime, since the +overall operation is asynchronous already, but may affect assertions in +tests. For example, if you want to assert that `$http` has been called +with specific arguments as a result of a `$resource` call, you now need +to run a `$digest` first, to ensure the (possibly empty) request +interceptor promise has been resolved. + +Before: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +After: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + $rootScope.$digest(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +### **$templateRequest**: + - due to **[c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**: give tpload error the correct namespace + +Previously the `tpload` error was namespaced to `$compile`. If you have +code that matches errors of the form `[$compile:tpload]` it will no +longer run. You should change the code to match +`[$templateRequest:tpload]`. + + - due to **([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**: always return the template that is stored in the cache + +The service now returns the result of `$templateCache.put()` when making a server request to the +template. Previously it would return the content of the response directly. +This now means if you are decorating `$templateCache.put()` to manipulate the template, you will +now get this manipulated result also on the first `$templateRequest` rather than only on subsequent +calls (when the template is retrived from the cache). +In practice this should not affect any apps, as it is unlikely that they rely on the template being +different in the first and subsequent calls. + +### **$animate** due to: + - **[16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**: let cancel() reject the runner promise + +$animate.cancel(runner) now rejects the underlying +promise and calls the catch() handler on the runner +returned by $animate functions (enter, leave, move, +addClass, removeClass, setClass, animate). +Previously it would resolve the promise as if the animation +had ended successfully. + +Example: + +```js +var runner = $animate.addClass('red'); +runner.then(function() { console.log('success')}); +runner.catch(function() { console.log('cancelled')}); + +runner.cancel(); +``` + +Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'. +To migrate, add a catch() handler to your animation runners. + +### **angular.isArray** due to: + - **[e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**: support Array subclasses in `angular.isArray()` + +Previously, `angular.isArray()` was an alias for `Array.isArray()`. +Therefore, objects that prototypally inherit from `Array` where not +considered arrays. Now such objects are considered arrays too. + +This change affects several other methods that use `angular.isArray()` +under the hood, such as `angular.copy()`, `angular.equals()`, +`angular.forEach()`, and `angular.merge()`. + +This in turn affects how dirty checking treats objects that prototypally +inherit from `Array` (e.g. MobX observable arrays). AngularJS will now +be able to handle these objects better when copying or watching. + +### **$sce** : + - due to **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service + +If you use `attrs.$set` for URL attributes (a[href] and img[src]) there will no +longer be any automated sanitization of the value. This is in line with other +programmatic operations, such as writing to the innerHTML of an element. + +If you are programmatically writing URL values to attributes from untrusted +input then you must sanitize it yourself. You could write your own sanitizer or copy +the private `$$sanitizeUri` service. + +Note that values that have been passed through the `$interpolate` service within the +`URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize +these values again. + + - due to **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service + +binding `trustAs()` and the short versions (`trustAsResourceUrl()` et al.) to +`ngSrc`, `ngSrcset`, and `ngHref` will now raise an infinite digest error: + +```js + $scope.imgThumbFn = function(id) { + return $sce.trustAsResourceUrl(someService.someUrl(id)); + }; +``` + +```html + +``` +This is because the `$interpolate` service is now responsible for sanitizing +the attribute value, and its watcher receives a new object from `trustAs()` +on every digest. +To migrate, compute the trusted value only when the input value changes: + +```js + $scope.$watch('imgId', function(id) { + $scope.imgThumb = $sce.trustAsResourceUrl(someService.someUrl(id)); + }); +``` + +```html + +``` + +### **orderBy** due to: + - **[1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**: consider `null` and `undefined` greater than other values + +When using `orderBy` to sort arrays containing `null` values, the `null` values +will be considered "greater than" all other values, except for `undefined`. +Previously, they were sorted as strings. This will result in different (but more +intuitive) sorting order. + +Before: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', null, 'o', 'z', undefined +``` + +After: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', 'o', 'z', null, undefined +``` + +### **ngScenario** due to: + - **[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**: completely remove the angular scenario runner + +The angular scenario runner end-to-end test framework has been +removed from the project and will no longer be available on npm +or bower starting with 1.7.0. +It was deprecated and removed from the documentation in 2014. +Applications that still use it should migrate to +[Protractor](http://www.protractortest.org). +Technically, it should also be possible to continue using an +older version of the scenario runner, as the underlying APIs have +not changed. However, we do not guarantee future compatibility. + +### **form** due to: + - **[223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**: set $submitted to true on child forms when parent is submitted + +Forms will now set $submitted on child forms when they are submitted. +For example: +``` +
+ + + + +
+``` + +Submitting this form will set $submitted on "parentform" and "childform". +Previously, it was only set on "parentform". + +This change was introduced because mixing form and ngForm does not create +logically separate forms, but rather something like input groups. +Therefore, child forms should inherit the submission state from their parent form. + +### **ngAria** due to: + - **[6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**: do not set aria attributes on input[type="hidden"] + +ngAria no longer sets aria-* attributes on input[type="hidden"] with ngModel. +This can affect apps that test for the presence of aria attributes on hidden inputs. +To migrate, remove these assertions. +In actual apps, this should not have a user-facing effect, as the previous behavior +was incorrect, and the new behavior is correct for accessibility. + +### **ngModel, input** due to: + - **[74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**: improve handling of built-in named parsers + +*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month", +"time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and +the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no +longer set `ngModelController.$error.number` and the `ng-invalid-number` class. + +Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and +`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers +and custom parsers easier. + +### **ngModelOptions** due to: + - **[55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**: add debounce catch-all + allow debouncing 'default' only + +the 'default' key in 'debounce' now only debounces the default event, i.e. the event +that is added as an update trigger by the different input directives automatically. + +Previously, it also applied to other update triggers defined in 'updateOn' that +did not have a corresponding key in the 'debounce'. + +This behavior is now supported via a special wildcard / catch-all key: '*'. + +See the following example: + +Pre-1.7: +'mouseup' is also debounced by 500 milliseconds because 'default' is applied: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500, 'blur': 0 } +} +``` + +1.7: +The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { '*': 500, 'blur': 0 } +} +``` + +In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500 } +} +``` + +### **input\[number\]** due to: + - **[aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**: validate min/max against viewValue + +`input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against +the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`. + +This affects apps that use `$parsers` or `$formatters` to transform the input / model value. + +If you rely on the $modelValue validation, you can overwrite the `min`/`max` validator from a custom directive, as seen in the following example directive definition object: + +``` +{ + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + var maxValidator = ctrl.$validators.max; + + ctrl.$validators.max = function(modelValue, viewValue) { + return maxValidator(modelValue, modelValue); + }; + } +} +``` + +### **input** due to: + - **[656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**: listen on "change" instead of "click" for radio/checkbox ngModels + +`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. +Most apps should not be affected, as "change" is automatically fired by browsers after "click" +happens. + +Two scenarios might need migration: + +- Custom click events: + +Before this change, custom click event listeners on radio / checkbox would be called after the +input element and `ngModel` had been updated, unless they were specifically registered before +the built-in click handlers. +After this change, they are called before the input is updated, and can call event.preventDefault() +to prevent the input from updating. + +If an app uses a click event listener that expects ngModel to be updated when it is called, it now +needs to register a change event listener instead. + +- Triggering click events: + +Conventional trigger functions: + +The change event might not be fired when the input element is not attached to the document. This +can happen in **tests** that compile input elements and +trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, +the change event will not be fired when the input isn't attached to the document. + +Before: + +```js + it('should update the model', inject(function($compile, $rootScope) { + var inputElm = $compile('')($rootScope); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +With this patch, `$rootScope.checkbox` might not be true, because the click event +hasn't triggered the change event. To make the test, work append the inputElm to the app's +`$rootElement`, and the `$rootElement` to the `$document`. + +After: + +```js + it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { + var inputElm = $compile('')($rootScope); + + $rootElement.append(inputElm); + $document.append($rootElement); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +`triggerHandler()`: + +If you are using this jQuery / jqLite function on the input elements, you don't have to attach +the elements to the document, but instead change the triggered event to "change". This is because +`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite. + +### **ngStyle** due to: + - **[15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**: use $watchCollection + +Previously the use of deep watch by ng-style would trigger styles to be +re-applied when nested state changed. Now only changes to direct +properties of the watched object will trigger changes. + +### **$compile** due to: + - **[38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**: remove the preAssignBindingsEnabled flag + +Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported. +The flag controlled whether bindings were available inside the controller +constructor or only in the `$onInit` hook. The bindings are now no longer +available in the constructor. + +To migrate your code: + +1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you +don't have to do anything to migrate. + +2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you +can remove that statement - since AngularJS 1.6.0 this is the default so your +app should still work even in AngularJS 1.6 after such removal. Afterwards, +migrating to AngularJS 1.7.0 shouldn't require any further action. + +3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need +to first migrate your code so that the flag can be flipped to `false`. The +instructions on how to do that are available in the "Migrating from 1.5 to 1.6" +guide: +https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6 +Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)` +statement. + + - **[6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**: lower the `xlink:href` security context for SVG's `a` and `image` elements + +In the unlikely case that an app relied on RESOURCE_URL whitelisting for the +purpose of binding to the `xlink:href` property of SVG's `` or `` +elements and if the values do not pass the regular URL sanitization, they will +break. + +To fix this you need to ensure that the values used for binding to the affected +`xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in +`$compileProvider`'s `aHrefSanitizationWhitelist` (for `` elements) or +`imgSrcSanitizationWhitelist` (for `` elements). + + - **[fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**: do not use deepWatch in literal one-way bindings + +Previously when a literal value was passed into a directive/component via +one-way binding it would be watched with a deep watcher. + +For example, for ``, a new instance of the array +would be passed into the directive/component (and trigger $onChanges) not +only if `a` changed but also if any sub property of `a` changed such as +`a.b` or `a.b.c.d.e` etc. + +This also means a new but equal value for `a` would NOT trigger such a +change. + +Now literal values use an input-based watch similar to other directive/component +one-way bindings. In this context inputs are the non-constant parts of the +literal. In the example above the input would be `a`. Changes are only +triggered when the inputs to the literal change. + + - **[1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**: add `base[href]` to the list of RESOURCE_URL context attributes + +Previously, `` would not require `baseUrl` to +be trusted as a RESOURCE_URL. Now, `baseUrl` will be sent to `$sce`'s +RESOURCE_URL checks. By default, it will break unless `baseUrl` is of the same +origin as the application document. + +Refer to the +[`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce) +for more info on how to trust a value in a RESOURCE_URL context. + +Also, concatenation in trusted contexts is not allowed, which means that the +following won't work: ``. + +Either construct complex values in a controller (recommended): + +```js +this.baseUrl = '/something/' + this.partialPath; +``` +```html + +``` + +Or use string concatenation in the interpolation expression (not recommended +except for the simplest of cases): + +```html + +``` + +### **ngTouch** due to: + - **[11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**: remove ngClick override, `$touchProvider`, and `$touch` + +The `ngClick` directive from the ngTouch module has been removed, and with it the +corresponding `$touchProvider` and `$touch` service. + +If you have included ngTouch v1.5.0 or higher in your application, and have not +changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch` +service, then there are no migration steps for your code. Otherwise you must remove references to +the provider and service. + +The `ngClick` override directive had been deprecated and by default disabled since v1.5.0, +because of buggy behavior in edge cases, and a general trend to avoid special touch based +overrides of click events. In modern browsers, it should not be necessary to use a touch override +library: + +- Chrome, Firefox, Edge, and Safari remove the 300ms delay when + `` is set. +- Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the + `touch-action` css property is set to `manipulation`. + +You can find out more in these articles: +https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away +https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8 +https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/ + +### **Angular** due to: + - **[1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**: remove `angular.lowercase` and `angular.uppercase` + +The helper functions `angular.lowercase` `and angular.uppercase` have +been removed. + +These functions have been deprecated since 1.5.0. They are internally +used, but should not be exposed as they contain special locale handling +(for Turkish) to maintain internal consistency regardless of user-set locale. + +Developers should generally use the built-ins `toLowerCase` and `toUpperCase` +or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases. + +Further, we generally discourage using the angular.x helpers in application code. + +### **$controller** due to: + - **[e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**: remove instantiating controllers defined on window + +The option to instantiate controllers from constructors on the global `window` object +has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()` +method that could enable this behavior, has been removed. + +This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope +is bad. To migrate, remove the call to $controllerProvider.allowGlobals() in the config, and +register your controller via the Module API or the $controllerProvider, e.g. + +``` +angular.module('myModule', []).controller('myController', function() {...}); + +angular.module('myModule', []).config(function($controllerProvider) { + $controllerProvider.register('myController', function() {...}); +}); + +``` + +### **$rootScope** due to: + - **[c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)**: provide correct value of one-time bindings in watchGroup + +Previously when using `$watchGroup` the entries in `newValues` and +`oldValues` represented the *most recent change of each entry*. + +Now the entries in `oldValues` will always equal the `newValues` of the previous +call of the listener. This means comparing the entries in `newValues` and +`oldValues` can be used to determine which individual expressions changed. + +For example `$scope.$watchGroup(['a', 'b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [1, undefined] | + + +Now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [2, undefined] | + +Note the last call now shows `a === 2` in the `oldValues` array. + +This also makes the `oldValue` of one-time watchers more clear. Previously +the `oldValue` of a one-time watcher would remain `undefined` forever. For +example `$scope.$watchGroup(['a', '::b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [undefined, undefined] | +| `a=b=3` | [3, 2] | [1, undefined] | + +Where now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [1, undefined] | +| `a=b=3` | [3, 2] | [1, 2] | + +### **$interval** due to: + - **[a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**: throw when trying to cancel non-$interval promise + +`$interval.cancel()` will throw an error if called with a promise that +was not generated by `$interval()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // No error; interval NOT canceled. +``` + +After: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $interval(doSomething, 1000, 5); +var newPromise = promise.then(doSomethingElse); +$interval.cancel(promise); // Interval canceled. +``` + +### **$timeout** due to: + - **[336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**: throw when trying to cancel non-$timeout promise + +`$timeout.cancel()` will throw an error if called with a promise that +was not generated by `$timeout()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // No error; timeout NOT canceled. +``` + +After: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $timeout(doSomething, 1000); +var newPromise = promise.then(doSomethingElse); +$timeout.cancel(promise); // Timeout canceled. +``` + + +# 1.7.0-rc.0 maximum-overdrive (2018-04-19) + +## Bug Fixes +- **input:** + - listen on "change" instead of "click" for radio/checkbox ngModels + ([656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e), + [#4516](https://github.com/angular/angular.js/issues/4516), + [#14667](https://github.com/angular/angular.js/issues/14667), + [#14685](https://github.com/angular/angular.js/issues/14685)) +- **input\[number\]:** validate min/max against viewValue + ([aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec), + [#12761](https://github.com/angular/angular.js/issues/12761), + [#16325](https://github.com/angular/angular.js/issues/16325)) +- **jqLite:** make removeData() not remove event handlers + ([b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a), + [#15869](https://github.com/angular/angular.js/issues/15869), + [#16512](https://github.com/angular/angular.js/issues/16512)) +- **$compile:** + - remove the preAssignBindingsEnabled flag + ([38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb), + [#15782](https://github.com/angular/angular.js/issues/15782)) + - add `base[href]` to the list of RESOURCE_URL context attributes + ([1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e), + [#15597](https://github.com/angular/angular.js/issues/15597)) +- **$interval:** throw when trying to cancel non-$interval promise + ([a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$timeout:** throw when trying to cancel non-$timeout promise + ([336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828), + [#16424](https://github.com/angular/angular.js/issues/16424), + [#16476](https://github.com/angular/angular.js/issues/16476)) +- **$cookies:** remove the deprecated $cookieStore factory + ([73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77), + [#16465](https://github.com/angular/angular.js/issues/16465)) +- **$resource:** fix interceptors and success/error callbacks + ([ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd), + [#6731](https://github.com/angular/angular.js/issues/6731), + [#9334](https://github.com/angular/angular.js/issues/9334), + [#6865](https://github.com/angular/angular.js/issues/6865), + [#16446](https://github.com/angular/angular.js/issues/16446)) +- **$templateRequest:** + - give tpload error the correct namespace + ([c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)) + - always return the template that is stored in the cache + ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e), + [#16225](https://github.com/angular/angular.js/issues/16225)) +- **$animate:** let cancel() reject the runner promise + ([16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83), + [#14204](https://github.com/angular/angular.js/issues/14204), + [#16373](https://github.com/angular/angular.js/issues/16373)) +- **ngTouch:** + - deprecate the module and its contents + ([67f54b](https://github.com/angular/angular.js/commit/67f54b660038de2b4346b3e76d66a8dc8ccb1f9b), + [#16427](https://github.com/angular/angular.js/issues/16427), + [#16431](https://github.com/angular/angular.js/issues/16431)) + - remove ngClick override, `$touchProvider`, and `$touch` + ([11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50), + [#15761](https://github.com/angular/angular.js/issues/15761), + [#15755](https://github.com/angular/angular.js/issues/15755)) +- **ngScenario:** completely remove the angular scenario runner + ([0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60), + [#9405](https://github.com/angular/angular.js/issues/9405)) +- **form:** set $submitted to true on child forms when parent is submitted + ([223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77), + [#10071](https://github.com/angular/angular.js/issues/10071)) +- **$rootScope:** + - provide correct value of one-time bindings in watchGroup + ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)) +- **ngAria:** do not set aria attributes on input[type="hidden"] + ([6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b), + [#15113](https://github.com/angular/angular.js/issues/15113), + [#16367](https://github.com/angular/angular.js/issues/16367)) +- **ngModel, input:** improve handling of built-in named parsers + ([74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140), + [#14292](https://github.com/angular/angular.js/issues/14292), + [#10076](https://github.com/angular/angular.js/issues/10076), + [#16347](https://github.com/angular/angular.js/issues/16347)) +- **$httpParamSerializerJQLike:** + - call functions as jQuery does + ([a784fa](https://github.com/angular/angular.js/commit/a784fab605d825f1158c6292b3c42f8c4a502fdf), + [#16138](https://github.com/angular/angular.js/issues/16138), + [#16139](https://github.com/angular/angular.js/issues/16139)) + - follow jQuery for `null` and `undefined` + ([301fdd](https://github.com/angular/angular.js/commit/301fdda648680d89ccab607c413a7ddede7b0165)) +- **$parse:** + - do not pass scope/locals to interceptors of one-time bindings + ([87a586](https://github.com/angular/angular.js/commit/87a586eb9a23cfd0d0bb681cc778b4b8e5c8451d)) + - always pass the intercepted value to watchers + ([2ee503](https://github.com/angular/angular.js/commit/2ee5033967d5f87a516bad137686b0592e25d26b), + [#16021](https://github.com/angular/angular.js/issues/16021)) + - respect the interceptor.$stateful flag + ([de7403](https://github.com/angular/angular.js/commit/de74034ddf6f92505ccdb61be413a6df2c723f87)) +- **Angular:** remove `angular.lowercase` and `angular.uppercase` + ([1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de), + [#15445](https://github.com/angular/angular.js/issues/15445)) +- **$controller:** remove instantiating controllers defined on window + ([e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16), + [#15349](https://github.com/angular/angular.js/issues/15349), + [#15762](https://github.com/angular/angular.js/issues/15762)) + + +## New Features +- **angular.isArray:** support Array subclasses in `angular.isArray()` + ([e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948), + [#15533](https://github.com/angular/angular.js/issues/15533), + [#15541](https://github.com/angular/angular.js/issues/15541)) +- **$sce:** handle URL sanitization through the `$sce` service + ([1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)) +- **orderBy:** consider `null` and `undefined` greater than other values + ([1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8), + [#15294](https://github.com/angular/angular.js/issues/15294), + [#16376](https://github.com/angular/angular.js/issues/16376)) +- **$resource:** add support for `request` and `requestError` interceptors (#15674) + ([240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded), + [#5146](https://github.com/angular/angular.js/issues/5146)) +- **ngModelOptions:** add debounce catch-all + allow debouncing 'default' only + ([55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68), + [#15411](https://github.com/angular/angular.js/issues/15411), + [#16335](https://github.com/angular/angular.js/issues/16335)) +- **$compile:** lower the `xlink:href` security context for SVG's `a` and `image` elements + ([6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd), + [#15736](https://github.com/angular/angular.js/issues/15736)) + + +## Performance Improvements +- **$rootScope:** allow $watchCollection use of expression input watching + ([97b00c](https://github.com/angular/angular.js/commit/97b00ca497676aaff8a803762a9f8c7ff4aa24dd)) +- **ngStyle:** use $watchCollection + ([15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0), + [#15947](https://github.com/angular/angular.js/issues/15947)) +- **$compile:** do not use deepWatch in literal one-way bindings + ([fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727), + [#15301](https://github.com/angular/angular.js/issues/15301)) + + + + +## Breaking Changes + +### **jqLite** due to: + - **[b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**: make removeData() not remove event handlers + +Before this commit `removeData()` invoked on an element removed its event +handlers as well. If you want to trigger a full cleanup of an element, change: + +```js +elem.removeData(); +``` + +to: + +```js +angular.element.cleanData(elem); +``` + +In most cases, though, cleaning up after an element is supposed to be done +only when it's removed from the DOM as well; in such cases the following: + +```js +elem.remove(); +``` + +will remove event handlers as well. + +### **$cookies** due to: + - **[73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**: remove the deprecated $cookieStore factory + +The $cookieStore has been removed. Migrate to the $cookies service. Note that +for object values you need to use the `putObject` & `getObject` methods as +`get`/`put` will not correctly save/retrieve them. + +Before: +```js +$cookieStore.put('name', {key: 'value'}); +$cookieStore.get('name'); // {key: 'value'} +$cookieStore.remove('name'); +``` + +After: +```js +$cookies.putObject('name', {key: 'value'}); +$cookies.getObject('name'); // {key: 'value'} +$cookies.remove('name'); +``` + +### **$resource** due to: + - **[ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**: fix interceptors and success/error callbacks + +If you are not using `success` or `error` callbacks with `$resource`, +your app should not be affected by this change. + +If you are using `success` or `error` callbacks (with or without +response interceptors), one (subtle) difference is that throwing an +error inside the callbacks will not propagate to the returned +`$promise`. Therefore, you should try to use the promises whenever +possible. E.g.: + +```js +// Avoid +User.query(function onSuccess(users) { throw new Error(); }). + $promise. + catch(function onError() { /* Will not be called. */ }); + +// Prefer +User.query(). + $promise. + then(function onSuccess(users) { throw new Error(); }). + catch(function onError() { /* Will be called. */ }); +``` + +Finally, if you are using `success` or `error` callbacks with response +interceptors, the callbacks will now always run _after_ the interceptors +(and wait for them to resolve in case they return a promise). +Previously, the `error` callback was called before the `responseError` +interceptor and the `success` callback was synchronously called after +the `response` interceptor. E.g.: + +```js +var User = $resource('/api/users/:id', {id: '@id'}, { + get: { + method: 'get', + interceptor: { + response: function(response) { + console.log('responseInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseInterceptor-2'); + return response.resource; + }); + }, + responseError: function(response) { + console.log('responseErrorInterceptor-1'); + return $timeout(1000).then(function() { + console.log('responseErrorInterceptor-2'); + return $q.reject('Ooops!'); + }); + } + } + } +}); +var onSuccess = function(value) { console.log('successCallback', value); }; +var onError = function(error) { console.log('errorCallback', error); }; + +// Assuming the following call is successful... +User.get({id: 1}, onSuccess, onError); + // Old behavior: + // responseInterceptor-1 + // successCallback, {/* Promise object */} + // responseInterceptor-2 + // New behavior: + // responseInterceptor-1 + // responseInterceptor-2 + // successCallback, {/* User object */} + +// Assuming the following call returns an error... +User.get({id: 2}, onSuccess, onError); + // Old behavior: + // errorCallback, {/* Response object */} + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // New behavior: + // responseErrorInterceptor-1 + // responseErrorInterceptor-2 + // errorCallback, Ooops! +``` + + - **[240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**: add support for `request` and `requestError` interceptors (#15674) + +Previously, calling a `$resource` method would synchronously call +`$http`. Now, it will be called asynchronously (regardless if a +`request`/`requestError` interceptor has been defined. + +This is not expected to affect applications at runtime, since the +overall operation is asynchronous already, but may affect assertions in +tests. For example, if you want to assert that `$http` has been called +with specific arguments as a result of a `$resource` call, you now need +to run a `$digest` first, to ensure the (possibly empty) request +interceptor promise has been resolved. + +Before: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +After: +```js +it('...', function() { + $httpBackend.expectGET('/api/things').respond(...); + var Things = $resource('/api/things'); + Things.query(); + $rootScope.$digest(); + + expect($http).toHaveBeenCalledWith(...); +}); +``` + +### **$templateRequest**: + - due to **[c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**: give tpload error the correct namespace + +Previously the `tpload` error was namespaced to `$compile`. If you have +code that matches errors of the form `[$compile:tpload]` it will no +longer run. You should change the code to match +`[$templateRequest:tpload]`. + + - due to **([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**: always return the template that is stored in the cache + +The service now returns the result of `$templateCache.put()` when making a server request to the +template. Previously it would return the content of the response directly. +This now means if you are decorating `$templateCache.put()` to manipulate the template, you will +now get this manipulated result also on the first `$templateRequest` rather than only on subsequent +calls (when the template is retrived from the cache). +In practice this should not affect any apps, as it is unlikely that they rely on the template being +different in the first and subsequent calls. + +### **$animate** due to: + - **[16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**: let cancel() reject the runner promise + +$animate.cancel(runner) now rejects the underlying +promise and calls the catch() handler on the runner +returned by $animate functions (enter, leave, move, +addClass, removeClass, setClass, animate). +Previously it would resolve the promise as if the animation +had ended successfully. + +Example: + +```js +var runner = $animate.addClass('red'); +runner.then(function() { console.log('success')}); +runner.catch(function() { console.log('cancelled')}); + +runner.cancel(); +``` + +Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'. +To migrate, add a catch() handler to your animation runners. + +### **angular.isArray** due to: + - **[e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**: support Array subclasses in `angular.isArray()` + +Previously, `angular.isArray()` was an alias for `Array.isArray()`. +Therefore, objects that prototypally inherit from `Array` where not +considered arrays. Now such objects are considered arrays too. + +This change affects several other methods that use `angular.isArray()` +under the hood, such as `angular.copy()`, `angular.equals()`, +`angular.forEach()`, and `angular.merge()`. + +This in turn affects how dirty checking treats objects that prototypally +inherit from `Array` (e.g. MobX observable arrays). AngularJS will now +be able to handle these objects better when copying or watching. + +### **$sce** due to: + - **[1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**: handle URL sanitization through the `$sce` service + +If you use `attrs.$set` for URL attributes (a[href] and img[src]) there will no +longer be any automated sanitization of the value. This is in line with other +programmatic operations, such as writing to the innerHTML of an element. + +If you are programmatically writing URL values to attributes from untrusted +input then you must sanitize it yourself. You could write your own sanitizer or copy +the private `$$sanitizeUri` service. + +Note that values that have been passed through the `$interpolate` service within the +`URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize +these values again. + +### **orderBy** due to: + - **[1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**: consider `null` and `undefined` greater than other values + +When using `orderBy` to sort arrays containing `null` values, the `null` values +will be considered "greater than" all other values, except for `undefined`. +Previously, they were sorted as strings. This will result in different (but more +intuitive) sorting order. + +Before: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', null, 'o', 'z', undefined +``` + +After: +```js +orderByFilter(['a', undefined, 'o', null, 'z']); +//--> 'a', 'o', 'z', null, undefined +``` + +### **ngScenario** due to: + - **[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**: completely remove the angular scenario runner + +The angular scenario runner end-to-end test framework has been +removed from the project and will no longer be available on npm +or bower starting with 1.7.0. +It was deprecated and removed from the documentation in 2014. +Applications that still use it should migrate to +[Protractor](http://www.protractortest.org). +Technically, it should also be possible to continue using an +older version of the scenario runner, as the underlying APIs have +not changed. However, we do not guarantee future compatibility. + +### **form** due to: + - **[223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**: set $submitted to true on child forms when parent is submitted + +Forms will now set $submitted on child forms when they are submitted. +For example: +``` +
+ + + + +
+``` + +Submitting this form will set $submitted on "parentform" and "childform". +Previously, it was only set on "parentform". + +This change was introduced because mixing form and ngForm does not create +logically separate forms, but rather something like input groups. +Therefore, child forms should inherit the submission state from their parent form. + +### **ngAria** due to: + - **[6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**: do not set aria attributes on input[type="hidden"] + +ngAria no longer sets aria-* attributes on input[type="hidden"] with ngModel. +This can affect apps that test for the presence of aria attributes on hidden inputs. +To migrate, remove these assertions. +In actual apps, this should not have a user-facing effect, as the previous behavior +was incorrect, and the new behavior is correct for accessibility. + +### **ngModel, input** due to: + - **[74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**: improve handling of built-in named parsers + +*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month", +"time", "datetime-local", "week", do no longer set `ngModelController.$error[inputType]`, and +the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" do no +longer set `ngModelController.$error.number` and the `ng-invalid-number` class. + +Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and +`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers +and custom parsers easier. + +### **ngModelOptions** due to: + - **[55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**: add debounce catch-all + allow debouncing 'default' only + +the 'default' key in 'debounce' now only debounces the default event, i.e. the event +that is added as an update trigger by the different input directives automatically. + +Previously, it also applied to other update triggers defined in 'updateOn' that +did not have a corresponding key in the 'debounce'. + +This behavior is now supported via a special wildcard / catch-all key: '*'. + +See the following example: + +Pre-1.7: +'mouseup' is also debounced by 500 milliseconds because 'default' is applied: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500, 'blur': 0 } +} +``` + +1.7: +The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { '*': 500, 'blur': 0 } +} +``` + +In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced: +``` +ng-model-options="{ + updateOn: 'default blur mouseup', + debounce: { 'default': 500 } +} +``` + +### **input\[number\]** due to: + - **[aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**: validate min/max against viewValue + +`input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against +the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`. + +This affects apps that use `$parsers` or `$formatters` to transform the input / model value. + +If you rely on the $modelValue validation, you can overwrite the `min`/`max` validator from a custom directive, as seen in the following example directive definition object: + +``` +{ + restrict: 'A', + require: 'ngModel', + link: function(scope, element, attrs, ctrl) { + var maxValidator = ctrl.$validators.max; + + ctrl.$validators.max = function(modelValue, viewValue) { + return maxValidator(modelValue, modelValue); + }; + } +} +``` + +### **input** due to: + - **[656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**: listen on "change" instead of "click" for radio/checkbox ngModels + +`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event. +Most apps should not be affected, as "change" is automatically fired by browsers after "click" +happens. + +Two scenarios might need migration: + +- Custom click events: + +Before this change, custom click event listeners on radio / checkbox would be called after the +input element and `ngModel` had been updated, unless they were specifically registered before +the built-in click handlers. +After this change, they are called before the input is updated, and can call event.preventDefault() +to prevent the input from updating. + +If an app uses a click event listener that expects ngModel to be updated when it is called, it now +needs to register a change event listener instead. + +- Triggering click events: + +Conventional trigger functions: + +The change event might not be fired when the input element is not attached to the document. This +can happen in **tests** that compile input elements and +trigger click events on them. Depending on the browser (Chrome and Safari) and the trigger method, +the change event will not be fired when the input isn't attached to the document. + +Before: + +```js + it('should update the model', inject(function($compile, $rootScope) { + var inputElm = $compile('')($rootScope); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +With this patch, `$rootScope.checkbox` might not be true, because the click event +hasn't triggered the change event. To make the test, work append the inputElm to the app's +`$rootElement`, and the `$rootElement` to the `$document`. + +After: + +```js + it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) { + var inputElm = $compile('')($rootScope); + + $rootElement.append(inputElm); + $document.append($rootElement); + + inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger() + expect($rootScope.checkbox).toBe(true); + }); +``` + +`triggerHandler()`: + +If you are using this jQuery / jqLite function on the input elements, you don't have to attach +the elements to the document, but instead change the triggered event to "change". This is because +`triggerHandler(event)` only triggers the exact event when it has been added by jQuery / jqLite. + +### **ngStyle** due to: + - **[15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**: use $watchCollection + +Previously the use of deep watch by ng-style would trigger styles to be +re-applied when nested state changed. Now only changes to direct +properties of the watched object will trigger changes. + +### **$compile** due to: + - **[38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**: remove the preAssignBindingsEnabled flag + +Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported. +The flag controlled whether bindings were available inside the controller +constructor or only in the `$onInit` hook. The bindings are now no longer +available in the constructor. + +To migrate your code: + +1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you +don't have to do anything to migrate. + +2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you +can remove that statement - since AngularJS 1.6.0 this is the default so your +app should still work even in AngularJS 1.6 after such removal. Afterwards, +migrating to AngularJS 1.7.0 shouldn't require any further action. + +3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need +to first migrate your code so that the flag can be flipped to `false`. The +instructions on how to do that are available in the "Migrating from 1.5 to 1.6" +guide: +https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6 +Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)` +statement. + + - **[6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**: lower the `xlink:href` security context for SVG's `a` and `image` elements + +In the unlikely case that an app relied on RESOURCE_URL whitelisting for the +purpose of binding to the `xlink:href` property of SVG's `` or `` +elements and if the values do not pass the regular URL sanitization, they will +break. + +To fix this you need to ensure that the values used for binding to the affected +`xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in +`$compileProvider`'s `aHrefSanitizationWhitelist` (for `` elements) or +`imgSrcSanitizationWhitelist` (for `` elements). + + - **[fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**: do not use deepWatch in literal one-way bindings + +Previously when a literal value was passed into a directive/component via +one-way binding it would be watched with a deep watcher. + +For example, for ``, a new instance of the array +would be passed into the directive/component (and trigger $onChanges) not +only if `a` changed but also if any sub property of `a` changed such as +`a.b` or `a.b.c.d.e` etc. + +This also means a new but equal value for `a` would NOT trigger such a +change. + +Now literal values use an input-based watch similar to other directive/component +one-way bindings. In this context inputs are the non-constant parts of the +literal. In the example above the input would be `a`. Changes are only +triggered when the inputs to the literal change. + + - **[1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**: add `base[href]` to the list of RESOURCE_URL context attributes + +Previously, `` would not require `baseUrl` to +be trusted as a RESOURCE_URL. Now, `baseUrl` will be sent to `$sce`'s +RESOURCE_URL checks. By default, it will break unless `baseUrl` is of the same +origin as the application document. + +Refer to the +[`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce) +for more info on how to trust a value in a RESOURCE_URL context. + +Also, concatenation in trusted contexts is not allowed, which means that the +following won't work: ``. + +Either construct complex values in a controller (recommended): + +```js +this.baseUrl = '/something/' + this.partialPath; +``` +```html + +``` + +Or use string concatenation in the interpolation expression (not recommended +except for the simplest of cases): + +```html + +``` + +### **ngTouch** due to: + - **[11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**: remove ngClick override, `$touchProvider`, and `$touch` + +The `ngClick` directive from the ngTouch module has been removed, and with it the +corresponding `$touchProvider` and `$touch` service. + +If you have included ngTouch v1.5.0 or higher in your application, and have not +changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch` +service, then there are no migration steps for your code. Otherwise you must remove references to +the provider and service. + +The `ngClick` override directive had been deprecated and by default disabled since v1.5.0, +because of buggy behavior in edge cases, and a general trend to avoid special touch based +overrides of click events. In modern browsers, it should not be necessary to use a touch override +library: + +- Chrome, Firefox, Edge, and Safari remove the 300ms delay when + `` is set. +- Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the + `touch-action` css property is set to `manipulation`. + +You can find out more in these articles: +https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away +https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8 +https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/ + +### **Angular** due to: + - **[1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**: remove `angular.lowercase` and `angular.uppercase` + +The helper functions `angular.lowercase` `and angular.uppercase` have +been removed. + +These functions have been deprecated since 1.5.0. They are internally +used, but should not be exposed as they contain special locale handling +(for Turkish) to maintain internal consistency regardless of user-set locale. + +Developers should generally use the built-ins `toLowerCase` and `toUpperCase` +or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases. + +Further, we generally discourage using the angular.x helpers in application code. + +### **$controller** due to: + - **[e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**: remove instantiating controllers defined on window + +The option to instantiate controllers from constructors on the global `window` object +has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()` +method that could enable this behavior, has been removed. + +This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope +is bad. To migrate, remove the call to $controllerProvider.allowGlobals() in the config, and +register your controller via the Module API or the $controllerProvider, e.g. + +``` +angular.module('myModule', []).controller('myController', function() {...}); + +angular.module('myModule', []).config(function($controllerProvider) { + $controllerProvider.register('myController', function() {...}); +}); + +``` + +### **$rootScope** due to: + - **[c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570)**: provide correct value of one-time bindings in watchGroup + +Previously when using `$watchGroup` the entries in `newValues` and +`oldValues` represented the *most recent change of each entry*. + +Now the entries in `oldValues` will always equal the `newValues` of the previous +call of the listener. This means comparing the entries in `newValues` and +`oldValues` can be used to determine which individual expressions changed. + +For example `$scope.$watchGroup(['a', 'b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [1, undefined] | + + +Now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `a=2` | [2, undefined] | [1, undefined] | +| `b=3` | [2, 3] | [2, undefined] | + +Note the last call now shows `a === 2` in the `oldValues` array. + +This also makes the `oldValue` of one-time watchers more clear. Previously +the `oldValue` of a one-time watcher would remain `undefined` forever. For +example `$scope.$watchGroup(['a', '::b'], fn)` would previously: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [undefined, undefined] | +| `a=b=3` | [3, 2] | [1, undefined] | + +Where now the `oldValue` will always equal the previous `newValue`: + +| Action | newValue | oldValue | +|----------|------------|------------| +| (init) | [undefined, undefined] | [undefined, undefined] | +| `a=1` | [1, undefined] | [undefined, undefined] | +| `b=2` | [1, 2] | [1, undefined] | +| `a=b=3` | [3, 2] | [1, 2] | + +### **$interval** due to: + - **[a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**: throw when trying to cancel non-$interval promise + +`$interval.cancel()` will throw an error if called with a promise that +was not generated by `$interval()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // No error; interval NOT canceled. +``` + +After: +```js +var promise = $interval(doSomething, 1000, 5).then(doSomethingElse); +$interval.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $interval(doSomething, 1000, 5); +var newPromise = promise.then(doSomethingElse); +$interval.cancel(promise); // Interval canceled. +``` + +### **$timeout** due to: + - **[336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**: throw when trying to cancel non-$timeout promise + +`$timeout.cancel()` will throw an error if called with a promise that +was not generated by `$timeout()`. Previously, it would silently do +nothing. + +Before: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // No error; timeout NOT canceled. +``` + +After: +```js +var promise = $timeout(doSomething, 1000).then(doSomethingElse); +$timeout.cancel(promise); // Throws error. +``` + +Correct usage: +```js +var promise = $timeout(doSomething, 1000); +var newPromise = promise.then(doSomethingElse); +$timeout.cancel(promise); // Timeout canceled. +``` + + + +# 1.6.10 crystalline-persuasion (2018-04-17) + +## Bug Fixes +- **$compile:** + - correctly handle `null`/`undefined` href `attrs.$set()` + ([f04e04](https://github.com/angular/angular.js/commit/f04e04e0e63e0d30c29718abd5cae634901793b2), + [#16520](https://github.com/angular/angular.js/issues/16520)) + - throw error in `$onChanges` immediately + ([b7d1e0fbd](https://github.com/angular/angular.js/commit/983e27b628fd1eab653e2b3966d90a270f27cc93), + [#15578](https://github.com/angular/angular.js/issues/15578), + [#16492](https://github.com/angular/angular.js/issues/16492)) +- **input:** + - allow overriding timezone for date input types + ([4355de](https://github.com/angular/angular.js/commit/4355dee21d26667bb7f6f21bf75c081351315033), + [#16181](https://github.com/angular/angular.js/issues/16181), + [#13382](https://github.com/angular/angular.js/issues/13382), + [#16336](https://github.com/angular/angular.js/issues/16336)) + - take timezone into account when validating minimum and maximum in date types + ([2f0ac6](https://github.com/angular/angular.js/commit/2f0ac696cb09aec3e291bb8c9c8a1092cbe3a061), + [#16342](https://github.com/angular/angular.js/issues/16342), + [#16390](https://github.com/angular/angular.js/issues/16390)) + - fix composition mode in IE for Korean input + ([9a1b7c](https://github.com/angular/angular.js/commit/9a1b7c9fa135d1dae3f9b4ccf48f081675796e92), + [#6656](https://github.com/angular/angular.js/issues/6656), + [#16273](https://github.com/angular/angular.js/issues/16273)) +- **jqLite:** use XHTML-compliant HTML as input for jqLite + ([a0c55a](https://github.com/angular/angular.js/commit/a0c55af9858075ab268a88dd7a4464788a46f4b7), + [#6917](https://github.com/angular/angular.js/issues/6917), + [#16518](https://github.com/angular/angular.js/issues/16518)) +- **minErr:** update url to https + ([52e466](https://github.com/angular/angular.js/commit/52e46683bfcc0ce0dc9a3d2ee42b389508423799)) +- **$http:** set correct xhrStatus in response when using 'timeout' + ([1faf7e](https://github.com/angular/angular.js/commit/1faf7ec30d55bba107b18efbcf0ef07732c55b91)) +- **browserTrigger:** support CompositionEvent + ([c33fd1](https://github.com/angular/angular.js/commit/c33fd1325417fdc6d7d6abc90cd935130653b149)) + + +## New Features +- **$http:** support sending XSRF token to whitelisted origins + ([bc7757](https://github.com/angular/angular.js/commit/bc775759c88b2221c2bb71d2335bc233c93f43b0), + [#7862](https://github.com/angular/angular.js/issues/7862)) +- **minErr:** strip error url from error parameters + ([980b69](https://github.com/angular/angular.js/commit/980b69dcae73dd8a3d0b9d91b63fa7711cd0ba36)) +- **$sanitize:** support enhancing elements/attributes white-lists + ([ee8e05](https://github.com/angular/angular.js/commit/ee8e05cfafe086188fc318ed4115fb56ba335112), + [#5900](https://github.com/angular/angular.js/issues/5900), + [#16326](https://github.com/angular/angular.js/issues/16326)) +- **$rootScope:** allow suspending and resuming watchers on scope + ([efb822c58](https://github.com/angular/angular.js/commit/41d5c90f170cc054b0f8f88220c22ef1ef6cc0a6), + [#16308](https://github.com/angular/angular.js/issues/5301)) + + +# 1.6.9 fiery-basilisk (2018-02-02) + + +## Bug Fixes +- **input:** add `drop` event support for IE + ([5dc076](https://github.com/angular/angular.js/commit/5dc07667de00c5e85fd69c5b7b7fe4fb5fd65a77)) +- **ngMessages:** prevent memory leak from messages that are never attached + ([9d058d](https://github.com/angular/angular.js/commit/9d058de04bb78694b83179e9b97bc40214eca01a), + [#16389](https://github.com/angular/angular.js/issues/16389), + [#16404](https://github.com/angular/angular.js/issues/16404), + [#16406](https://github.com/angular/angular.js/issues/16406)) +- **ngTransclude:** remove terminal: true + ([1d826e](https://github.com/angular/angular.js/commit/1d826e2f1e941d14c3c56d7a0249f5796ba11f85), + [#16411](https://github.com/angular/angular.js/issues/16411), + [#16412](https://github.com/angular/angular.js/issues/16412)) +- **$sanitize:** sanitize `xml:base` attributes + ([b9ef65](https://github.com/angular/angular.js/commit/b9ef6585e10477fbbf912a971fe0b390bca692a6)) + + +## New Features +- **currencyFilter:** trim whitespace around an empty currency symbol + ([367390](https://github.com/angular/angular.js/commit/3673909896efb6ff47546caf7fc61549f193e043), + [#15018](https://github.com/angular/angular.js/issues/15018), + [#15085](https://github.com/angular/angular.js/issues/15085), + [#15105](https://github.com/angular/angular.js/issues/15105)) + + + +# 1.6.8 beneficial-tincture (2017-12-18) + + +## Bug Fixes +- **$location:** + - always decode special chars in `$location.url(value)` + ([2bdf71](https://github.com/angular/angular.js/commit/2bdf7126878c87474bb7588ce093d0a3c57b0026)) + - decode non-component special chars in Hashbang URLS + ([57b626](https://github.com/angular/angular.js/commit/57b626a673b7530399d3377dfe770165bec35f8a)) +- **ngModelController:** allow $overrideModelOptions to set updateOn + ([55516d](https://github.com/angular/angular.js/commit/55516da2dfc7c5798dce24e9fa930c5ac90c900c), + [#16351](https://github.com/angular/angular.js/issues/16351), + [#16364](https://github.com/angular/angular.js/issues/16364)) + + +## New Features +- **$parse:** add a hidden interface to retrieve an expression's AST + ([f33d95](https://github.com/angular/angular.js/commit/f33d95cfcff6fd0270f92a142df8794cca2013ad), + [#16253](https://github.com/angular/angular.js/issues/16253), + [#16260](https://github.com/angular/angular.js/issues/16260)) + + +# 1.6.7 imperial-backstroke (2017-11-24) + + +## Bug Fixes +- **$compile:** sanitize special chars in directive name + ([c4003f](https://github.com/angular/angular.js/commit/c4003fd03489f876b646f06838f4edb576bacf6f), + [#16314](https://github.com/angular/angular.js/issues/16314), + [#16278](https://github.com/angular/angular.js/issues/16278)) +- **$location:** do not decode forward slashes in the path in HTML5 mode + ([e06ebf](https://github.com/angular/angular.js/commit/e06ebfdbb558544602fe9da4d7d98045a965f468), + [#16312](https://github.com/angular/angular.js/issues/16312)) +- **sanitizeUri:** sanitize URIs that contain IDEOGRAPHIC SPACE chars + ([ddeb1d](https://github.com/angular/angular.js/commit/ddeb1df15a23de93eb95dbe202e83e93673e1c4e), + [#16288](https://github.com/angular/angular.js/issues/16288)) +- **$rootScope:** fix potential memory leak when removing scope listeners + ([358a69](https://github.com/angular/angular.js/commit/358a69fa8b89b251ee44e523458d6c7f40b92b2d), + [#16135](https://github.com/angular/angular.js/issues/16135), + [#16161](https://github.com/angular/angular.js/issues/16161)) +- **http:** do not allow encoded callback params in jsonp requests + ([569e90](https://github.com/angular/angular.js/commit/569e906a5818271416ad0b749be2f58dc34938bd)) +- **ngMock:** pass unexpected request failures in `$httpBackend` to the error handler + ([1555a4](https://github.com/angular/angular.js/commit/1555a4911ad5360c145c0ddc8ec6c4bf9a381c13), + [#16150](https://github.com/angular/angular.js/issues/16150), + [#15855](https://github.com/angular/angular.js/issues/15855)) +- **ngAnimate:** don't close transitions when child transitions close + ([1391e9](https://github.com/angular/angular.js/commit/1391e99c7f73795180b792af21ad4402f96e225d), + [#16210](https://github.com/angular/angular.js/issues/16210)) +- **ngMock.browserTrigger:** add 'bubbles' to Transition/Animation Event + ([7a5f06](https://github.com/angular/angular.js/commit/7a5f06d55d123a39bb7b030667fb1ab672939598)) + + +## New Features +- **$sanitize, $compileProvider, linky:** add support for the "sftp" protocol in links + ([a675ea](https://github.com/angular/angular.js/commit/a675ea034366fbb0fcf0d73fed65216aa99bce11), + [#16102](https://github.com/angular/angular.js/issues/16102)) +- **ngModel.NgModelController:** expose $processModelValue to run model -> view pipeline + ([145194](https://github.com/angular/angular.js/commit/14519488ce9218aa891d34e89fc3271fd4ed0f04), + [#3407](https://github.com/angular/angular.js/issues/3407), + [#10764](https://github.com/angular/angular.js/issues/10764), + [#16237](https://github.com/angular/angular.js/issues/16237)) +- **$injector:** ability to load new modules after bootstrapping + ([6e78fe](https://github.com/angular/angular.js/commit/6e78fee73258bb0ae36414f9db2e8734273e481b)) + + +## Performance Improvements +- **jqLite:** + - avoid setting class attribute when not changed + ([9c95f6](https://github.com/angular/angular.js/commit/9c95f6d5e00ee7e054aabb3e363f5bfb3b7b4103)) + - avoid repeated add/removeAttribute in jqLiteRemoveClass + ([cab9eb](https://github.com/angular/angular.js/commit/cab9ebfd5a02e897f802bf6321b8471e4843c5d3), + [#16078](https://github.com/angular/angular.js/issues/16078), + [#16131](https://github.com/angular/angular.js/issues/16131)) + + + +# 1.6.6 interdimensional-cable (2017-08-18) + + +## Bug Fixes +- **$httpParamSerializer:** ignore functions + ([b51ded](https://github.com/angular/angular.js/commit/b51ded67366865f36c5781dd5d9b801488ec95ea), + [#16133](https://github.com/angular/angular.js/issues/16133)) +- **$resource:** do not throw when calling old `$cancelRequest()` + ([009ebe](https://github.com/angular/angular.js/commit/009ebec64c81d11b280c635167050e8906e191c6), + [#16037](https://github.com/angular/angular.js/issues/16037)) +- **$parse:** + - do not shallow-watch computed property keys + ([750465](https://github.com/angular/angular.js/commit/7504656a26202de591e4ac9674333254304edf8a)) + - support constants in computed keys + ([9d6c3f](https://github.com/angular/angular.js/commit/9d6c3f3ec233279885e37a250d25860d5c15f716)) +- **$http:** do not throw error if `Content-Type` is not `application/json` but response is JSON-like + ([2e1163](https://github.com/angular/angular.js/commit/2e1163ef5cb56d1933e8ecd7b74020b9df9c6693), + [#16027](https://github.com/angular/angular.js/issues/16027), + [#16075](https://github.com/angular/angular.js/issues/16075)) + + +## New Features +- **$compile:** add `strictComponentBindingsEnabled()` method + ([3ec181](https://github.com/angular/angular.js/commit/3ec1819b913c8edf0649e06217dbd5920f29f126), + [#16129](https://github.com/angular/angular.js/issues/16129)) +- **$resource:** add resource to response for error interceptors + ([9256db](https://github.com/angular/angular.js/commit/9256dbc4201343ce5cd63a9eadf98da4793f45af), + [#16109](https://github.com/angular/angular.js/issues/16109)) +- **$http:** allow differentiation between XHR completion, error, abort, timeout + ([5e2bc5](https://github.com/angular/angular.js/commit/5e2bc5bbf347a9dfadc08b1514b8be06fd550913), + [#15924](https://github.com/angular/angular.js/issues/15924), + [#15847](https://github.com/angular/angular.js/issues/15847)) + + + +# 1.6.5 toffee-salinization (2017-07-03) + + +## Bug Fixes +- **core:** + - correctly detect Error instances from different contexts + ([6daca0](https://github.com/angular/angular.js/commit/6daca023e42098f7098b9bf153c8e53a17af84f1), + [#15868](https://github.com/angular/angular.js/issues/15868), + [#15872](https://github.com/angular/angular.js/issues/15872)) + - deprecate `angular.merge` + ([dc41f4](https://github.com/angular/angular.js/commit/dc41f465baae9bc91418a61f446596157c530b6e), + [#12653](https://github.com/angular/angular.js/issues/12653), + [#14941](https://github.com/angular/angular.js/issues/14941), + [#15180](https://github.com/angular/angular.js/issues/15180), + [#15992](https://github.com/angular/angular.js/issues/15992), + [#16036](https://github.com/angular/angular.js/issues/16036)) +- **ngOptions:** + - re-render after empty option has been removed + ([510d0f](https://github.com/angular/angular.js/commit/510d0f946fa1a443ad43fa31bc9337676ef31332)) + - allow empty option to be removed and re-added + ([71b4da](https://github.com/angular/angular.js/commit/71b4daa4e10b6912891927ee2a7930c604b538f8)) + - select unknown option if unmatched model does not match empty option + ([17d34b](https://github.com/angular/angular.js/commit/17d34b7a983a0ef63f6cf404490385c696fb0da1)) +- **orderBy:** guarantee stable sort + ([e50ed4](https://github.com/angular/angular.js/commit/e50ed4da9e8177168f67da68bdf02f07da4e7bcf), + [#14881](https://github.com/angular/angular.js/issues/14881), + [#15914](https://github.com/angular/angular.js/issues/15914)) +- **$parse:** + - do not shallow-watch inputs to one-time intercepted expressions + ([6e3b5a](https://github.com/angular/angular.js/commit/6e3b5a57cd921823f3eca7200a79ac5c2ef0567a)) + - standardize one-time literal vs non-literal and interceptors + ([f003d9](https://github.com/angular/angular.js/commit/f003d93a3dd052dccddef41125d9c51034ac3605)) + - do not shallow-watch inputs when wrapped in an interceptor fn + ([aac562](https://github.com/angular/angular.js/commit/aac5623247a86681cbe0e1c8179617b816394c1d), + [#15905](https://github.com/angular/angular.js/issues/15905)) + - always re-evaluate filters within literals when an input is an object + ([ec9768](https://github.com/angular/angular.js/commit/ec97686f2f4a5481cc806462313a664fc7a1c893), + [#15964](https://github.com/angular/angular.js/issues/15964), + [#15990](https://github.com/angular/angular.js/issues/15990)) +- **$sanitize:** use appropriate inert document strategy for Firefox and Safari + ([8f31f1](https://github.com/angular/angular.js/commit/8f31f1ff43b673a24f84422d5c13d6312b2c4d94)) +- **$timeout/$interval:** do not trigger a digest on cancel + ([a222d0](https://github.com/angular/angular.js/commit/a222d0b452622624dc498ef0b9d3c43647fd4fbc), + [#16057](https://github.com/angular/angular.js/issues/16057), + [#16064](https://github.com/angular/angular.js/issues/16064))
+ This change might affect the use of `$timeout.flush()` in unit tests. See the commit message for + more info. +- **ngMock/$interval:** add support for zero-delay intervals in tests + ([a1e3f8](https://github.com/angular/angular.js/commit/a1e3f8728e0a80396f980e48f8dc68dde6721b2b), + [#15952](https://github.com/angular/angular.js/issues/15952), + [#15953](https://github.com/angular/angular.js/issues/15953)) +- **angular-loader:** do not depend on "closure" globals that may not be available + ([a3226d](https://github.com/angular/angular.js/commit/a3226d01fadaf145713518dc5b8022b581c34e81), + [#15880](https://github.com/angular/angular.js/issues/15880), + [#15881](https://github.com/angular/angular.js/issues/15881)) + + +## New Features +- **select:** expose info about selection state in controller + ([0b962d](https://github.com/angular/angular.js/commit/0b962d4881e98327a91c37f7317da557aa991663), + [#13172](https://github.com/angular/angular.js/issues/13172), + [#10127](https://github.com/angular/angular.js/issues/10127)) +- **$animate:** add support for `customFilter` + ([ab114a](https://github.com/angular/angular.js/commit/ab114af8508bdbdb1fa5fd1e070d08818d882e28), + [#14891](https://github.com/angular/angular.js/issues/14891)) +- **$compile:** overload `.component()` to accept object map of components + ([210112](https://github.com/angular/angular.js/commit/2101126ce72308d8fc468ca2411bb9972e614f79), + [#14579](https://github.com/angular/angular.js/issues/14579), + [#16062](https://github.com/angular/angular.js/issues/16062)) +- **$log:** log all parameters in IE 9, not just the first two. + ([3671a4](https://github.com/angular/angular.js/commit/3671a43be43d05b00c90dfb3a3f746c013139581)) +- **ngMock:** describe unflushed http requests + ([d9128e](https://github.com/angular/angular.js/commit/d9128e7b2371ab2bb5169ba854b21c78baa784d2), + [#10596](https://github.com/angular/angular.js/issues/10596), + [#15928](https://github.com/angular/angular.js/issues/15928)) + + +## Performance Improvements +- **ngOptions:** prevent initial options repainting + ([ff52b1](https://github.com/angular/angular.js/commit/ff52b188a759f2cc7ee6ee78a8c646c2354a47eb), + [#15801](https://github.com/angular/angular.js/issues/15801), + [#15812](https://github.com/angular/angular.js/issues/15812), + [#16071](https://github.com/angular/angular.js/issues/16071)) +- **$animate:** + - avoid unnecessary computations if animations are globally disabled + ([ce5ffb](https://github.com/angular/angular.js/commit/ce5ffbf667464bd58eae4c4af0917eb2685f1f6a), + [#14914](https://github.com/angular/angular.js/issues/14914)) + - do not retrieve `className` unless `classNameFilter` is used + ([275978](https://github.com/angular/angular.js/commit/27597887379a1904cd86832602e286894b449a75)) + + + + +# 1.6.4 phenomenal-footnote (2017-03-31) + + +## Bug Fixes +- **$parse:** + - standardize one-time literal vs non-literal and interceptors + ([60394a](https://github.com/angular/angular.js/commit/60394a9d91dad8932fa900af7c8529837f1d4557), + [#15858](https://github.com/angular/angular.js/issues/15858)) + - fix infinite digest errors when watching objects with .valueOf in literals + ([f5ddb1](https://github.com/angular/angular.js/commit/f5ddb10b56676c2ad912ce453acb87f0a7a94e01), + [#15867](https://github.com/angular/angular.js/issues/15867)) +- **ngModel:** prevent internal scope reference from being copied + ([e1f8a6](https://github.com/angular/angular.js/commit/e1f8a6e82bb8a70079ef3db9a891b1c08b5bae31), + [#15833](https://github.com/angular/angular.js/issues/15833)) +- **jqLite:** make jqLite invoke jqLite.cleanData as a method + ([9cde98](https://github.com/angular/angular.js/commit/9cde98cbc770f8d33fc074ba563b7ab6e2baaf8b), + [#15846](https://github.com/angular/angular.js/issues/15846)) +- **$http:** throw more informative error on invalid JSON response + ([df8887](https://github.com/angular/angular.js/commit/df88873bb79213057057adb47151b626a7ec0e5d), + [#15695](https://github.com/angular/angular.js/issues/15695), + [#15724](https://github.com/angular/angular.js/issues/15724)) +- **dateFilter:** correctly handle newlines in `format` string + ([982271](https://github.com/angular/angular.js/commit/9822711ad2a401c2449239edc13d18b301714757), + [#15794](https://github.com/angular/angular.js/issues/15794), + [#15792](https://github.com/angular/angular.js/issues/15792)) + + +## New Features +- **$resource:** add `hasBody` action configuration option + ([a9f987](https://github.com/angular/angular.js/commit/a9f987a0c9653246ea471a89197907d94c0cea2a), + [#10128](https://github.com/angular/angular.js/issues/10128), + [#12181](https://github.com/angular/angular.js/issues/12181)) + + + +# 1.6.3 scriptalicious-bootstrapping (2017-03-08) + + +## Bug Fixes +- **AngularJS:** + - do not auto-bootstrap if the `src` exists but is empty + ([3536e8](https://github.com/angular/angular.js/commit/3536e83d8a085b02bd6dcec8324800b7e6c734e4)) + - do not auto bootstrap if the currentScript has been clobbered + ([95f964](https://github.com/angular/angular.js/commit/95f964b827b6f5b5aab10af54f7831316c7a9935)) + - do not auto-bootstrap if the script source is bad and inside SVG + ([c8f78a](https://github.com/angular/angular.js/commit/c8f78a8ca9debc33a6deaf951f344b8d372bf210)) +- **$log:** don't parse error stacks manually outside of IE/Edge + ([64e5af](https://github.com/angular/angular.js/commit/64e5afc4786fdfd850c6bdb488a5aa2b8b077f74), + [#15590](https://github.com/angular/angular.js/issues/15590), + [#15767](https://github.com/angular/angular.js/issues/15767)) +- **$sanitize:** prevent clobbered elements from freezing the browser + ([3bb1dd](https://github.com/angular/angular.js/commit/3bb1dd5d7f7dcde6fea5a3148f8f10e92f451e9d), + [#15699](https://github.com/angular/angular.js/issues/15699)) +- **$animate:** + - reset `classNameFilter` to `null` when a disallowed RegExp is used + ([a584fb](https://github.com/angular/angular.js/commit/a584fb6e1569fc1dd85e23b251a7c126edc2dd5b), + [#14913](https://github.com/angular/angular.js/issues/14913)) + - improve detection on `ng-animate` in `classNameFilter` RegExp + ([1f1331](https://github.com/angular/angular.js/commit/1f13313f403381581e1c31c57ebfe7a96546c6e4), + [#14806](https://github.com/angular/angular.js/issues/14806)) +- **filterFilter:** don't throw if `key.charAt` is not a function + ([f27d19](https://github.com/angular/angular.js/commit/f27d19ed606bf05ba41698159ebbc5fbc195033e), + [#15644](https://github.com/angular/angular.js/issues/15644), + [#15660](https://github.com/angular/angular.js/issues/15660)) +- **select:** + - add attribute "selected" for `select[multiple]` + ([851367](https://github.com/angular/angular.js/commit/8513674911300b27d518383a905fde9b3f25f7ae)) + - keep original selection when using shift to add options in IE/Edge + ([97b74a](https://github.com/angular/angular.js/commit/97b74ad6fbcbc4b63e37e9eb44962d6f8de83e8b), + [#15675](https://github.com/angular/angular.js/issues/15675), + [#15676](https://github.com/angular/angular.js/issues/15676)) +- **$jsonpCallbacks:** allow `$window` to be mocked in unit tests + ([5ca0de](https://github.com/angular/angular.js/commit/5ca0de64873c32ab2f540a3226e73c4175a15c50), + [#15685](https://github.com/angular/angular.js/issues/15685), + [#15686](https://github.com/angular/angular.js/issues/15686)) + + +## New Features +- **info:** add `angularVersion` info to each module + ([1e582e](https://github.com/angular/angular.js/commit/1e582e4fa486f340150bba95927f1b26d9142de2)) +- **$injector:** add new `modules` property + ([742123](https://github.com/angular/angular.js/commit/7421235f247e5b7113345401bc5727cfbf81ddc2)) +- **Module:** add `info()` method + ([09ba69](https://github.com/angular/angular.js/commit/09ba69078de6ba52c70571b82b6205929f6facc5), + [#15225](https://github.com/angular/angular.js/issues/15225)) +- **errorHandlingConfig:** make the depth for object stringification in errors configurable + ([4a5eaf](https://github.com/angular/angular.js/commit/4a5eaf7bec85ceca8b934ebaff4d1834a1a09f57), + [#15402](https://github.com/angular/angular.js/issues/15402), + [#15433](https://github.com/angular/angular.js/issues/15433)) + + + +# 1.6.2 llamacorn-lovehug (2017-02-07) + + +## Bug Fixes +- **$compile:** + - do not swallow thrown errors in testsg + ([0377c6](https://github.com/angular/angular.js/commit/0377c6f0e890cb4ed3eb020b96720b4b34f75df3), + [#15629](https://github.com/angular/angular.js/issues/15629), + [#15631](https://github.com/angular/angular.js/issues/15631)) + - allow the usage of "$" in isolate scope property alias + ([7f2af3](https://github.com/angular/angular.js/commit/7f2af3f923e7a3f85c8862d0ed57d21c72eae904), + [#15594](https://github.com/angular/angular.js/issues/15594)) +- **$location:** correctly handle external URL change during `$digest` + ([b60761](https://github.com/angular/angular.js/commit/b607618342d6c4fab364966fe05f152be6bd4d5f), + [#11075](https://github.com/angular/angular.js/issues/11075), + [#12571](https://github.com/angular/angular.js/issues/12571), + [#15556](https://github.com/angular/angular.js/issues/15556), + [#15561](https://github.com/angular/angular.js/issues/15561)) +- **$browser:** detect external changes in `history.state` + ([fa50fb](https://github.com/angular/angular.js/commit/fa50fbaf57b3437be7a410ecaba7008dbe0ef239)) +- **$resource:** + - do not swallow errors in `success` callback + ([27146e](https://github.com/angular/angular.js/commit/27146e8a7fad54c1342179b6d291b1b5c2ebe816), + [#15624](https://github.com/angular/angular.js/issues/15624), + [#15628](https://github.com/angular/angular.js/issues/15628)) + - correctly unescape `/\.` even if `\.` comes from a param value + ([419a48](https://github.com/angular/angular.js/commit/419a4813e354496bdf0df44e3f8afaa198df1ab1), + [#15627](https://github.com/angular/angular.js/issues/15627)) + - delete `$cancelRequest()` in `toJSON()` + ([086c5d](https://github.com/angular/angular.js/commit/086c5d0354db8cb3d106b9ff966fb48d6fb46ef8), + [#15244](https://github.com/angular/angular.js/issues/15244)) +- **$animate:** correctly animate transcluded clones with `templateUrl` + ([f01212](https://github.com/angular/angular.js/commit/f01212ab5287ac7a154da7d75037ed444e81eb34), + [#15510](https://github.com/angular/angular.js/issues/15510), + [#15514](https://github.com/angular/angular.js/issues/15514)) +- **$route:** make asynchronous tasks count as pending requests + ([eb968c](https://github.com/angular/angular.js/commit/eb968c4a6884838db05369a04459066424c5bba8), + [#14159](https://github.com/angular/angular.js/issues/14159)) +- **$parse:** make sure ES6 object computed properties are watched + ([5e418b](https://github.com/angular/angular.js/commit/5e418b1145a1045da598c7863e785d647ea83850), + [#15678](https://github.com/angular/angular.js/issues/15678)) +- **$sniffer:** allow `history` for NW.js apps + ([4a593d](https://github.com/angular/angular.js/commit/4a593db79ba1e21a6aa600a82cf6d757cad94d01), + [#15474](https://github.com/angular/angular.js/issues/15474), + [#15633](https://github.com/angular/angular.js/issues/15633)) +- **input:** fix `step` validation for `input[type=number/range]` + ([c95a67](https://github.com/angular/angular.js/commit/c95a6737fbd277e40c064bd9f68f383bf119505c), + [#15504](https://github.com/angular/angular.js/issues/15504), + [#15506](https://github.com/angular/angular.js/issues/15506)) +- **select:** keep `ngModel` when selected option is recreated by `ngRepeat` + ([131af8](https://github.com/angular/angular.js/commit/131af8272d269a541d04cb522c264a91e0ec8b6a), + [#15630](https://github.com/angular/angular.js/issues/15630), + [#15632](https://github.com/angular/angular.js/issues/15632)) +- **ngValue:** correctly update the `value` property when `value` is undefined + ([05aab6](https://github.com/angular/angular.js/commit/05aab660ce74f526f2110d3b5faf9a5b4f4e664b) + [#15603](https://github.com/angular/angular.js/issues/15603), + [#15605](https://github.com/angular/angular.js/issues/15605)) +- **angularInit:** allow auto-bootstrapping from inline script + ([bb464d](https://github.com/angular/angular.js/commit/bb464d16b434b9e2de2fecf80c192d4741cba879), + [#15567](https://github.com/angular/angular.js/issues/15567), + [#15571](https://github.com/angular/angular.js/issues/15571)) +- **ngMockE2E:** ensure that mocked `$httpBackend` uses correct `$browser` + ([bd63b2](https://github.com/angular/angular.js/commit/bd63b2235cd410251cb83eebd9a47d3102830b6b), + [#15593](https://github.com/angular/angular.js/issues/15593)) + + +## New Features +- **ngModel:** add `$overrideModelOptions` support + ([2546c2](https://github.com/angular/angular.js/commit/2546c29f811b68eea4d68be7fa1c8f7bb562dc11), + [#15415](https://github.com/angular/angular.js/issues/15415)) +- **$parse:** allow watching array/object literals with non-primitive values + ([25f008](https://github.com/angular/angular.js/commit/25f008f541d68b09efd7b428b648c6d4899e6972), + [#15301](https://github.com/angular/angular.js/issues/15301)) + + + + +# 1.5.11 princely-quest (2017-01-13) + + +## Bug Fixes +- **$compile:** allow the usage of "$" in isolate scope property alias + ([e75fbc](https://github.com/angular/angular.js/commit/e75fbc494e6a0da6a9231b40bb0382431b62be07), + [#15586](https://github.com/angular/angular.js/issues/15586), + [#15594](https://github.com/angular/angular.js/issues/15594)) +- **angularInit:** allow auto-bootstrapping from inline script + ([41aa91](https://github.com/angular/angular.js/commit/41aa9125b9aaf771addb250642f524a4e6f9d8d3), + [#15567](https://github.com/angular/angular.js/issues/15567), + [#15571](https://github.com/angular/angular.js/issues/15571)) +- **$resource:** delete `$cancelRequest()` in `toJSON()` + ([4f3858](https://github.com/angular/angular.js/commit/4f3858e7c371f87534397f45b9d002add33b00cc), + [#15244](https://github.com/angular/angular.js/issues/15244)) +- **$$cookieReader:** correctly handle forbidden access to `document.cookie` + ([6933cf](https://github.com/angular/angular.js/commit/6933cf64fe51f54b10d1639f2b95bab3c1178df9), + [#15523](https://github.com/angular/angular.js/issues/15523), + [#15532](https://github.com/angular/angular.js/issues/15532)) + + + + +# 1.6.1 promise-rectification (2016-12-23) + + +## Bug Fixes +- **$q:** Add traceback to unhandled promise rejections + ([174cb4](https://github.com/angular/angular.js/commit/174cb4a8c81e25581da5b452c2bb43b0fa377a9b), + [#14631](https://github.com/angular/angular.js/issues/14631)) +- **$$cookieReader:** correctly handle forbidden access to `document.cookie` + ([33f769](https://github.com/angular/angular.js/commit/33f769b0a1214055c16fb59adad4897bf53d62bf), + [#15523](https://github.com/angular/angular.js/issues/15523)) +- **ngOptions:** do not unset the `selected` property unless necessary + ([bc4844](https://github.com/angular/angular.js/commit/bc4844d3b297d80aecef89aa1b32615024decedc), + [#15477](https://github.com/angular/angular.js/issues/15477)) +- **ngModelOptions:** work correctly when on the template of `replace` directives + ([5f8ed6](https://github.com/angular/angular.js/commit/5f8ed63f2ab02ffb9c21bf9c29d27c851d162e26), + [#15492](https://github.com/angular/angular.js/issues/15492)) +- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously + ([d52864](https://github.com/angular/angular.js/commit/d528644fe3e9ffd43999e7fc67806059f9e1083e)) +- **jqLite:** silently ignore `after()` if element has no parent + ([3d68b9](https://github.com/angular/angular.js/commit/3d68b9502848ff6714ef89bfb95b8e70ae34eff6), + [#15331](https://github.com/angular/angular.js/issues/15331), + [#15475](https://github.com/angular/angular.js/issues/15475)) +- **$rootScope:** when adding/removing watchers during $digest + ([163aca](https://github.com/angular/angular.js/commit/163aca336d7586a45255787af41b14b2a12361dd), + [#15422](https://github.com/angular/angular.js/issues/15422)) + + +## Performance Improvements +- **ngClass:** avoid unnecessary `.data()` accesses, deep-watching and copies + ([1d3b65](https://github.com/angular/angular.js/commit/1d3b65adc2c22ff662159ef910089cf10d1edb7b), + [#14404](https://github.com/angular/angular.js/issues/14404)) + + + + +# 1.5.10 asynchronous-synchronization (2016-12-15) + + +## Bug Fixes +- **$compile:** + - don't throw tplrt error when there is whitespace around a top-level comment + ([12752f](https://github.com/angular/angular.js/commit/12752f66ac425ab38a5ee574a4bfbf3516adc42c), + [#15108](https://github.com/angular/angular.js/issues/15108)) + - clean up `@`-binding observers when re-assigning bindings + ([f3cb6e](https://github.com/angular/angular.js/commit/f3cb6e309aa1f676e5951ac745fa886d3581c2f4), + [#15268](https://github.com/angular/angular.js/issues/15268)) + - set attribute value even if `ngAttr*` contains no interpolation + ([229799](https://github.com/angular/angular.js/commit/22979904fb754c59e9f6ee5d8763e3b8de0e18c2), + [#15133](https://github.com/angular/angular.js/issues/15133)) + - `bindToController` should work without `controllerAs` + ([944989](https://github.com/angular/angular.js/commit/9449893763a4fd95ee8ff78b53c6966a874ec9ae), + [#15088](https://github.com/angular/angular.js/issues/15088)) + - do not overwrite values set in `$onInit()` for `<`-bound literals + ([07e1ba](https://github.com/angular/angular.js/commit/07e1ba365fb5e8a049be732bd7b62f71e0aa1672), + [#15118](https://github.com/angular/angular.js/issues/15118)) + - avoid calling `$onChanges()` twice for `NaN` initial values + ([0cf5be](https://github.com/angular/angular.js/commit/0cf5be52642f7e9d81a708b3005042eac6492572)) +- **$location:** prevent infinite digest with IDN urls in Edge + ([4bf892](https://github.com/angular/angular.js/commit/4bf89218130d434771089fdfe643490b8d2ee259), + [#15217](https://github.com/angular/angular.js/issues/15217)) +- **$rootScope:** correctly handle adding/removing watchers during `$digest` + ([a9708d](https://github.com/angular/angular.js/commit/a9708de84b50f06eacda33834d5bbdfc97c97f37), + [#15422](https://github.com/angular/angular.js/issues/15422)) +- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**` + ([78eecb](https://github.com/angular/angular.js/commit/78eecb43dbb0500358d333aea8955bd0646a7790)) +- **jqLite:** silently ignore `after()` if element has no parent + ([77ed85](https://github.com/angular/angular.js/commit/77ed85bcd3be057a5a79231565ac7accc6d644c6), + [#15331](https://github.com/angular/angular.js/issues/15331)) +- **input[radio]:** use non-strict comparison for checkedness + ([593a50](https://github.com/angular/angular.js/commit/593a5034841b3b7661d3bcbdd06b7a9d0876fd34)) +- **select, ngOptions:** + - let `ngValue` take precedence over option text with multiple interpolations + ([5b7ec8](https://github.com/angular/angular.js/commit/5b7ec8c84e88ee08aacaf9404853eda0016093f5), + [#15413](https://github.com/angular/angular.js/issues/15413)) + - don't add comment nodes as empty options + ([1d29c9](https://github.com/angular/angular.js/commit/1d29c91c3429de96e4103533752700d1266741be), + [#15454](https://github.com/angular/angular.js/issues/15454)) +- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously + ([e3d020](https://github.com/angular/angular.js/commit/e3d02070ab8a02c818dcc5114db6fba9d3f385d6)) +- **$sanitize:** reduce stack height in IE <= 11 + ([862dc2](https://github.com/angular/angular.js/commit/862dc2532f8126a4a71fd3d957884ba6f11f591c), + [#14928](https://github.com/angular/angular.js/issues/14928)) +- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()` + ([75c83f](https://github.com/angular/angular.js/commit/75c83ff3195931859a099f7a95bf81d32abf2eb3)) + + +## New Features +- **bootstrap:** do not bootstrap from unknown schemes with a different origin + ([bdeb33](https://github.com/angular/angular.js/commit/bdeb3392a8719131ab2b993f2a881c43a2860f92), + [#15428](https://github.com/angular/angular.js/issues/15428)) +- **$anchorScroll:** convert numeric hash targets to string + ([a52640](https://github.com/angular/angular.js/commit/a5264090b66ad0cf9a93de84bb7b307868c0edef), + [#14680](https://github.com/angular/angular.js/issues/14680)) +- **$compile:** + - add `preAssignBindingsEnabled` option + ([f86576](https://github.com/angular/angular.js/commit/f86576def44005f180a66e3aa12d6cc73c1ac72c)) + - throw error when directive name or factory function is invalid + ([5c9399](https://github.com/angular/angular.js/commit/5c9399d18ae5cd79e6cf6fc4377d66df00f6fcc7), + [#15056](https://github.com/angular/angular.js/issues/15056)) +- **$controller:** throw when requested controller is not registered + ([9ae793](https://github.com/angular/angular.js/commit/9ae793d8a69afe84370b601e07fc375fc18a576a), + [#14980](https://github.com/angular/angular.js/issues/14980)) +- **$location:** add support for selectively rewriting links based on attribute + ([a4a222](https://github.com/angular/angular.js/commit/a4a22266f127d3b9a6818e6f4754f048e253f693)) +- **$resource:** pass `status`/`statusText` to success callbacks + ([a8da25](https://github.com/angular/angular.js/commit/a8da25c74d2c1f6265f0fafd95bf72c981d9d678), + [#8341](https://github.com/angular/angular.js/issues/8841), + [#8841](https://github.com/angular/angular.js/issues/8841)) +- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator` + ([0e1651](https://github.com/angular/angular.js/commit/0e1651bfd28ba73ebd0e4943d85af48c4506e02c), + [#3410](https://github.com/angular/angular.js/issues/3410), + [#3516](https://github.com/angular/angular.js/issues/3516)) + + +## Performance Improvements +- **all:** don't trigger digests after enter/leave of structural directives + ([c57779](https://github.com/angular/angular.js/commit/c57779d8725493c5853dceda0105dafd5c0e3a7c), + [#15322](https://github.com/angular/angular.js/issues/15322)) +- **$compile:** validate `directive.restrict` property on directive init + ([31d464](https://github.com/angular/angular.js/commit/31d464feef38b1cc950da6c8dccd0f194ebfc68b)) +- **ngOptions:** avoid calls to `element.value` + ([e269ad](https://github.com/angular/angular.js/commit/e269ad1244bc50fee9218f7c18fab3e9ab063aab)) +- **jqLite:** move bind/unbind definitions out of the loop + ([7717b9](https://github.com/angular/angular.js/commit/7717b96e950a5916a5f12fd611c73d3b06a8d717)) + + + +# 1.6.0 rainbow-tsunami (2016-12-08) + +**Here are the full changes for the release of 1.6.0 that are not already released in the 1.5.x branch, +consolidating all the changes shown in the previous 1.6.0 release candidates.** + +## New Features +- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` + ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa), + [#10922](https://github.com/angular/angular.js/issues/10922)) +- **$compile:** + - add `preAssignBindingsEnabled` option + ([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef)) + - set `preAssignBindingsEnabled` to false by default + ([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858), + [#15352](https://github.com/angular/angular.js/issues/15352)) + - throw error when directive name or factory function is invalid + ([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec) + [#15056](https://github.com/angular/angular.js/issues/15056)) +- **jqLite:** + - implement `jqLite(f)` as an alias to `jqLite(document).ready(f)` + ([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e)) + - don't throw for elements with missing `getAttribute` + ([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417)) + - don't get/set properties when getting/setting boolean attributes + ([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304), + [#14126](https://github.com/angular/angular.js/issues/14126)) + - don't remove a boolean attribute for `.attr(attrName, '')` + ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)) + - remove the attribute for `.attr(attribute, null)` + ([4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)) + - return `[]` for `.val()` on ` + + + +``` + +The migration strategy is to convert values that matched with non-strict +conversion so that they will match with strict conversion. + + +- **feat(ngModelOptions): allow options to be inherited from ancestor `ngModelOptions` + ([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa))**: + +The programmatic API for `ngModelOptions` has changed. You must now read options +via the `ngModelController.$options.getOption(name)` method, rather than accessing the +option directly as a property of the `ngModelContoller.$options` object. This does not +affect the usage in templates and only affects custom directives that might have been +reading options for their own purposes. + +One benefit of these changes, though, is that the `ngModelControler.$options` property +is now guaranteed to be defined so there is no need to check before accessing. + +So, previously: + +``` +var myOption = ngModelController.$options && ngModelController.$options['my-option']; +``` + +and now: + +``` +var myOption = ngModelController.$options.getOption('my-option'); +``` + +### **jqLite** due to: +- **[fc0c11](https://github.com/angular/angular.js/commit/fc0c11db845d53061430b7f05e773dcb3fb5b860)**: + camelCase keys in `jqLite#data` + +Previously, keys passed to the data method were left untouched. +Now they are internally camelCased similarly to how jQuery handles it, i.e. +only single (!) hyphens followed by a lowercase letter get converted to an +uppercase letter. This means keys `a-b` and `aB` represent the same data piece; +writing to one of them will also be reflected if you ask for the other one. + +If you use Angular with jQuery, it already behaved in this way so no changes +are required on your part. + +To migrate the code follow the examples below: + +BEFORE: + +```js +/* 1 */ +elem.data('my-key', 2); +elem.data('myKey', 3); + +/* 2 */ +elem.data('foo-bar', 42); +elem.data()['foo-bar']; // 42 +elem.data()['fooBar']; // undefined + +/* 3 */ +elem.data()['foo-bar'] = 1; +elem.data()['fooBar'] = 2; +elem.data('foo-bar'); // 1 +``` + +AFTER: + +```js +/* 1 */ +// Rename one of the keys as they would now map to the same data slot. +elem.data('my-key', 2); +elem.data('my-key2', 3); + +/* 2 */ +elem.data('foo-bar', 42); +elem.data()['foo-bar']; // undefined +elem.data()['fooBar']; // 42 + +/* 3 */ +elem.data()['foo-bar'] = 1; +elem.data()['fooBar'] = 2; +elem.data('foo-bar'); // 2 +``` + +- **[73050c](https://github.com/angular/angular.js/commit/73050cdda04675bfa6705dc841ddbbb6919eb048)**: + align jqLite camelCasing logic with JQuery + +Before, when Angular was used without jQuery, the key passed +to the css method was more heavily camelCased; now only a single (!) hyphen +followed by a lowercase letter is getting transformed. This also affects APIs +that rely on the css method, like ngStyle. + +If you use Angular with jQuery, it already behaved in this way so no changes +are needed on your part. + +To migrate the code follow the example below: + +Before: + +HTML: + +```html +// All five versions used to be equivalent. +
+
+
+
+
+``` + +JS: + +```js +// All five versions used to be equivalent. +elem.css('background_color', 'blue'); +elem.css('background:color', 'blue'); +elem.css('background-color', 'blue'); +elem.css('background--color', 'blue'); +elem.css('backgroundColor', 'blue'); + +// All five versions used to be equivalent. +var bgColor = elem.css('background_color'); +var bgColor = elem.css('background:color'); +var bgColor = elem.css('background-color'); +var bgColor = elem.css('background--color'); +var bgColor = elem.css('backgroundColor'); +``` + +After: + +HTML: + +```html +// Previous five versions are no longer equivalent but these two still are. +
+
+``` + +JS: + +```js +// Previous five versions are no longer equivalent but these two still are. +elem.css('background-color', 'blue'); +elem.css('backgroundColor', 'blue'); + +// Previous five versions are no longer equivalent but these two still are. +var bgColor = elem.css('background-color'); +var bgColor = elem.css('backgroundColor'); +``` + +- **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes + +Previously, all boolean attributes were reflected into the corresponding property when calling a +setter and from the corresponding property when calling a getter, even on elements that don't treat +those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to +know when to reflect the property. Note that this browser-level conversion differs between browsers; +if you need to dynamically change the state of an element, you should modify the property, not the +attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed +description about a related change in jQuery 1.9. + +This change aligns jqLite with jQuery 3. To migrate the code follow the example below: + +Before: + +CSS: + +```css +input[checked="checked"] { ... } +``` + +JS: + +```js +elem1.attr('checked', 'checked'); +elem2.attr('checked', false); +``` + +After: + +CSS: + +```css +input:checked { ... } +``` + +JS: + +```js +elem1.prop('checked', true); +elem2.prop('checked', false); +``` + +- **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**: + don't remove a boolean attribute for `.attr(attrName, '')` + +Before, using the `attr` method with an empty string as a value +would remove the boolean attribute. Now it sets it to its lowercase name as +was happening for every non-empty string so far. The only two values that remove +the boolean attribute are now null & false, just like in jQuery. + +To migrate the code follow the example below: + +Before: + +```js +elem.attr(booleanAttrName, ''); +``` + +After: + +```js +elem.attr(booleanAttrName, false); +``` + +or: + +```js +elem.attr(booleanAttrName, null); +``` + +- **[4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa)**: + remove the attribute for `.attr(attribute, null)` + +Invoking `elem.attr(attributeName, null)` would set the +`attributeName` attribute value to a string `"null"`, now it removes the +attribute instead. + +To migrate the code follow the example below: + +Before: + +```js +elem.attr(attributeName, null); +``` + +After: + +```js +elem.attr(attributeName, "null"); +``` + +- **[d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8)**: + return [] for .val() on ` + + + +``` + +JavaScript: + +```js + var value = $element.val(); + if (value) { + /* do something */ + } +``` + +After: + +HTML: + +```html + +``` + +JavaScript: + +```js + var value = $element.val(); + if (value.length > 0) { + /* do something */ + } +``` + + +### `ngModel` due to: + +- **[7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b)**: + treat synchronous validators as boolean always + +Previously, only a literal `false` return would resolve as the +synchronous validator failing. Now, all falsy JavaScript values +are treated as failing the validator, as one would naturally expect. + +Specifically, the values `0` (the number zero), `null`, `NaN` and `''` (the +empty string) used to be considered valid (passing) and they are now considered +invalid (failing). The value `undefined` was treated similarly to a pending +asynchronous validator, causing the validation to be pending. `undefined` is +also now considered invalid. + +To migrate, make sure your synchronous validators are returning either a +literal `true` or a literal `false` value. For most code, we expect this to +already be the case. Only a very small subset of projects will be affected. + +Namely, anyone using `undefined` or any falsy value as a return will now see +their validation failing, whereas previously falsy values other than `undefined` +would have been seen as passing and `undefined` would have been seen as pending. + +- **[9e24e7](https://github.com/angular/angular.js/commit/9e24e774a558143b3478536911a3a4c1714564ba)**: + change controllers to use prototype methods + +The use of prototype methods instead of new methods per instance removes the ability to pass +NgModelController and FormController methods without context. + +For example + +```js +$scope.$watch('something', myNgModelCtrl.$render) +``` + +will no longer work because the `$render` method is passed without any context. +This must now be replaced with + +```js +$scope.$watch('something', function() { + myNgModelCtrl.$render(); +}) +``` + +or possibly by using `Function.prototype.bind` or `angular.bind`. + + +### `aria/ngModel` due to: + +- **[975a61](https://github.com/angular/angular.js/commit/975a6170efceb2a5e6377c57329731c0636eb8c8)**: + do not overwrite the default `$isEmpty()` method for checkboxes + +Custom `checkbox`-shaped controls (e.g. checkboxes, menuitemcheckboxes), no longer have a custom +`$isEmpty()` method on their `NgModelController` that checks for `value === false`. Unless +overwritten, the default `$isEmpty()` method will be used, which treats `undefined`, `null`, `NaN` +and `''` as "empty". + +**Note:** The `$isEmpty()` method is used to determine if the checkbox is checked ("not empty" means + "checked") and thus it can indirectly affect other things, such as the control's validity + with respect to the `required` validator (e.g. "empty" + "required" --> "invalid"). + +Before: + +```js +var template = ''; +var customCheckbox = $compile(template)(scope); +var ctrl = customCheckbox.controller('ngModel'); + +scope.$apply('value = false'); +console.log(ctrl.$isEmpty()); //--> true + +scope.$apply('value = true'); +console.log(ctrl.$isEmpty()); //--> false + +scope.$apply('value = undefined'/* or null or NaN or '' */); +console.log(ctrl.$isEmpty()); //--> false +``` + +After: + +```js +var template = ''; +var customCheckbox = $compile(template)(scope); +var ctrl = customCheckbox.controller('ngModel'); + +scope.$apply('value = false'); +console.log(ctrl.$isEmpty()); //--> false + +scope.$apply('value = true'); +console.log(ctrl.$isEmpty()); //--> false + +scope.$apply('value = undefined'/* or null or NaN or '' */); +console.log(ctrl.$isEmpty()); //--> true +``` + +-- +If you want to have a custom `$isEmpty()` method, you need to overwrite the default. For example: + +```js +.directive('myCheckbox', function myCheckboxDirective() { + return { + require: 'ngModel', + link: function myCheckboxPostLink(scope, elem, attrs, ngModelCtrl) { + ngModelCtrl.$isEmpty = function myCheckboxIsEmpty(value) { + return !value; // Any falsy value means "empty" + + // Or to restore the previous behavior: + // return value === false; + }; + } + }; +}) +``` + +### `$http` due to: +- **[b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014)**: + remove deprecated callback methods: `success()/error()` + +`$http`'s deprecated custom callback methods - `success()` and `error()` - have been removed. +You can use the standard `then()`/`catch()` promise methods instead, but note that the method +signatures and return values are different. + +`success(fn)` can be replaced with `then(fn)`, and `error(fn)` can be replaced with either +`then(null, fn)` or `catch(fn)`. + +Before: + +```js +$http(...). + success(function onSuccess(data, status, headers, config) { + // Handle success + ... + }). + error(function onError(data, status, headers, config) { + // Handle error + ... + }); +``` + +After: + +```js +$http(...). + then(function onSuccess(response) { + // Handle success + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }, function onError(response) { + // Handle error + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }); + +// or + +$http(...). + then(function onSuccess(response) { + // Handle success + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }). + catch(function onError(response) { + // Handle error + var data = response.data; + var status = response.status; + var statusText = response.statusText; + var headers = response.headers; + var config = response.config; + ... + }); +``` + +**Note:** +There is a subtle difference between the variations showed above. When using +`$http(...).success(onSuccess).error(onError)` or `$http(...).then(onSuccess, onError)`, the +`onError()` callback will only handle errors/rejections produced by the `$http()` call. If the +`onSuccess()` callback produces an error/rejection, it won't be handled by `onError()` and might go +unnoticed. In contrast, when using `$http(...).then(onSuccess).catch(onError)`, `onError()` will +handle errors/rejections produced by both `$http()` _and_ `onSuccess()`. + +- **[fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb)**: + JSONP callback must be specified by `jsonpCallbackParam` config + +You can no longer use the `JSON_CALLBACK` placeholder in your JSONP requests. +Instead you must provide the name of the query parameter that will pass the +callback via the `jsonpCallbackParam` property of the config object, or app-wide via +the `$http.defaults.jsonpCallbackParam` property, which is `"callback"` by default. + +Before this change: + +```js +$http.json('trusted/url?callback=JSON_CALLBACK'); +$http.json('other/trusted/url', {params: {cb:'JSON_CALLBACK'}}); +``` + +After this change: + +```js +$http.json('trusted/url'); +$http.json('other/trusted/url', {jsonpCallbackParam:'cb'}); +``` + +- **[6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4)**: + JSONP requests now require a trusted resource URL + +All JSONP requests now require the URL to be trusted as resource URLs. +There are two approaches to trust a URL: + +**Whitelisting with the `$sceDelegateProvider.resourceUrlWhitelist()` +method.** + +You configure this list in a module configuration block: + +```js +appModule.config(['$sceDelegateProvider', function($sceDelegateProvider) { + $sceDelegateProvider.resourceUrlWhitelist([ + // Allow same origin resource loads. + 'self', + // Allow JSONP calls that match this pattern + 'https://some.dataserver.com/**.jsonp?**' + ]); +}]); +``` + +**Explicitly trusting the URL via the `$sce.trustAsResourceUrl(url)` +method.** + +You can pass a trusted object instead of a string as a URL to the `$http` +service: + +```js +var promise = $http.jsonp($sce.trustAsResourceUrl(url)); +``` + +- **[4f6f2b](https://github.com/angular/angular.js/commit/4f6f2bce4ac93b85320e42e5023c09d099779b7d)**: + properly increment/decrement `$browser.outstandingRequestCount` + +HTTP requests now update the outstanding request count synchronously. +Previously the request count would not have been updated until the +request to the server is actually in flight. Now the request count is +updated before the async interceptor is called. + +The new behaviour is correct but it may change the expected behaviour in +a small number of e2e test cases where an async request interceptor is +being used. + + +### `$q` due to: + +- **[e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9)**: + treat thrown errors as regular rejections + +Previously, throwing an error from a promise's `onFulfilled` or `onRejection` handlers, would result +in passing the error to the `$exceptionHandler()` (in addition to rejecting the promise with the +error as reason). + +Now, a thrown error is treated exactly the same as a regular rejection. This applies to all +services/controllers/filters etc that rely on `$q` (including built-in services, such as `$http` and +`$route`). For example, `$http`'s `transformRequest/Response` functions or a route's `redirectTo` +function as well as functions specified in a route's `resolve` object, will no longer result in a +call to `$exceptionHandler()` if they throw an error. Other than that, everything will continue to +behave in the same way; i.e. the promises will be rejected, route transition will be cancelled, +`$routeChangeError` events will be broadcasted etc. + +- **[c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0)**: + report promises with non rejection callback + +Unhandled rejected promises will be logged to $exceptionHandler. + +Tests that depend on specific order or number of messages in $exceptionHandler +will need to handle rejected promises report. + + +### `ngTransclude` due to: + +- **[32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301)**: + use fallback content if only whitespace is provided + +Previously whitespace only transclusion would be treated as the transclusion +being "not empty", which meant that fallback content was not used in that +case. + +Now if you only provide whitespace as the transclusion content, it will be +assumed to be empty and the fallback content will be used instead. + +If you really do want whitespace then you can force it to be used by adding +a comment to the whitespace. + +Previously this would not fallback to default content: + +```html + + +``` + +Now the whitespace between the opening and closing tags is treated as empty. To force the +previous behaviour simply add a comment: + +```html + + +``` + + +### `$compile` due to: + +- **[13c252](https://github.com/angular/angular.js/commit/13c2522baf7c8f616b2efcaab4bffd54c8736591)**: + correctly merge consecutive text nodes on IE11 + +**Note:** Everything described below affects **IE11 only**. + +Previously, consecutive text nodes would not get merged if they had no parent. They will now, which +might have unexpected side effects in the following cases: + +1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly: + + ```js + // Assuming: + var textNodes = [ + document.createTextNode('{{'), + document.createTextNode('"foo:"'), + document.createTextNode('}}') + ]; + var compiledNodes = $compile(textNodes)($rootScope); + + // Before: + console.log(compiledNodes.length); // 3 + console.log(compiledNodes.text()); // {{'foo'}} + + // After: + console.log(compiledNodes.length); // 1 + console.log(compiledNodes.text()); // foo + + // To get the old behavior, compile each node separately: + var textNodes = [ + document.createTextNode('{{'), + document.createTextNode('"foo"'), + document.createTextNode('}}') + ]; + var compiledNodes = angular.element(textNodes.map(function (node) { + return $compile(node)($rootScope)[0]; + })); + ``` + +2. Using multi-slot transclusion with non-consecutive, default-content text nodes (that form + interpolated expressions when merged): + + ```js + // Assuming the following component: + .component('someThing', { + template: '' + transclude: { + ignored: 'veryImportantContent' + } + }) + ``` + + ```html + + + {{ + Nooot + 'foo'}} + + + + + + {{ <-- Two separate + 'foo'}} <-- text nodes + + + + + + + foo <-- The text nodes were merged into `{{'foo'}}`, which was then interpolated + + + + + + + {{ + Nooot + 'foo'}} + + + + + + {{ <-- Two separate + 'foo'}} <-- nodes + + + ``` + +- **[b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4)**: + move check for interpolation of `on-"event"` attributes to compile time + +Using interpolation in any on* event attributes (e.g. `' + - '' + - '
'); - - var form = doc.find('form'), - destroyed = false, - nextTurn = false, - submitted = false, - reloadPrevented; - - scope.destroy = function() { - // yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep - // this test small. Imagine that the destroy action will cause a model change (e.g. - // $location change) that will cause some directive to destroy the dom (e.g. ngView+$route) - doc.empty(); - destroyed = true; - }; - - scope.submitMe = function() { - submitted = true; - }; - - var assertPreventDefaultListener = function(e) { - reloadPrevented = e.defaultPrevented || (e.returnValue === false); - }; - - $compile(doc)(scope); - - addEventListenerFn(form[0], 'submit', assertPreventDefaultListener); - - browserTrigger(doc.find('button'), 'click'); - - // let the browser process all events (and potentially reload the page) - setTimeout(function() { nextTurn = true;}, 100); - - waitsFor(function() { return nextTurn; }); - - runs(function() { - expect(doc.html()).toBe(''); - expect(destroyed).toBe(true); - expect(submitted).toBe(false); // this is known corner-case that is not currently handled - // the issue is that the submit listener is destroyed before - // the event propagates there. we can fix this if we see - // the issue in the wild, I'm not going to bother to do it - // now. (i) - - // prevent mem leak in test - removeEventListenerFn(form[0], 'submit', assertPreventDefaultListener); + it('should prevent the default when the form is destroyed by a submission via a click event', function(done) { + inject(function($timeout) { + doc = jqLite('
' + + '
' + + '' + + '
' + + '
'); + // Support: Chrome 60+ (on Windows) + // We need to add the form to the DOM in order for `submit` events to be properly fired. + window.document.body.appendChild(doc[0]); + + var form = doc.find('form'), + destroyed = false, + nextTurn = false, + submitted = false, + reloadPrevented = 'never called'; + + scope.destroy = function() { + // yes, I know, scope methods should not do direct DOM manipulation, but I wanted to keep + // this test small. Imagine that the destroy action will cause a model change (e.g. + // $location change) that will cause some directive to destroy the dom (e.g. ngView+$route) + doc.empty(); + destroyed = true; + }; + + scope.submitMe = function() { + submitted = true; + }; + + var assertPreventDefaultListener = function(e) { + reloadPrevented = e.defaultPrevented || (e.returnValue === false); + }; + + $compile(doc)(scope); + + form[0].addEventListener('submit', assertPreventDefaultListener); + + browserTrigger(doc.find('button'), 'click'); + + // let the browser process all events (and potentially reload the page) + window.setTimeout(function() { nextTurn = true;}, 100); + + var job = createAsync(done); + job.waitsFor(function() { return nextTurn; }) + .runs(function() { + expect(doc.html()).toBe(''); + expect(destroyed).toBe(true); + expect(submitted).toBe(false); // this is known corner-case that is not currently handled + // the issue is that the submit listener is destroyed before + // the event propagates there. we can fix this if we see + // the issue in the wild, I'm not going to bother to do it + // now. (i) + + // Support: Chrome 60+ (on Windows) + // Chrome 60+ on Windows does not fire `submit` events when the form is not attached to + // the DOM. Verify that the `submit` listener was either never fired or (if fired) the + // reload was prevented. + expect(reloadPrevented).not.toBe(false); + + // prevent mem leak in test + form[0].removeEventListener('submit', assertPreventDefaultListener); + }) + .done(); + job.start(); }); - })); + }); it('should NOT prevent form submission if action attribute present', function() { - var callback = jasmine.createSpy('submit').andCallFake(function(event) { + var callback = jasmine.createSpy('submit').and.callFake(function(event) { expect(event.isDefaultPrevented()).toBe(false); event.preventDefault(); }); @@ -410,6 +539,113 @@ describe('form', function() { expect(parent.$submitted).toBeTruthy(); }); + it('should set $submitted to true on child forms when parent is submitted', function() { + doc = jqLite( + '' + + '' + + '' + + '' + + '' + + ''); + $compile(doc)(scope); + + var parent = scope.parent, + child = scope.child; + + parent.$setSubmitted(); + expect(parent.$submitted).toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + }); + + + it('should not propagate $submitted state on removed child forms when parent is submitted', function() { + doc = jqLite( + '' + + '' + + '' + + '' + + '' + + '' + + ''); + $compile(doc)(scope); + + var parent = scope.parent, + child = scope.child, + grandchild = scope.grandchild, + ggchild = scope.greatgrandchild; + + parent.$removeControl(child); + + parent.$setSubmitted(); + expect(parent.$submitted).toBeTruthy(); + expect(child.$submitted).not.toBeTruthy(); + expect(grandchild.$submitted).not.toBeTruthy(); + + parent.$addControl(child); + + expect(parent.$submitted).toBeTruthy(); + expect(child.$submitted).not.toBeTruthy(); + expect(grandchild.$submitted).not.toBeTruthy(); + + parent.$setSubmitted(); + expect(parent.$submitted).toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + expect(grandchild.$submitted).toBeTruthy(); + + parent.$removeControl(child); + + expect(parent.$submitted).toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + expect(grandchild.$submitted).toBeTruthy(); + + parent.$setPristine(); // sets $submitted to false + expect(parent.$submitted).not.toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + expect(grandchild.$submitted).toBeTruthy(); + + grandchild.$setPristine(); + expect(grandchild.$submitted).not.toBeTruthy(); + + child.$setSubmitted(); + expect(parent.$submitted).not.toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + expect(grandchild.$submitted).toBeTruthy(); + + child.$setPristine(); + expect(parent.$submitted).not.toBeTruthy(); + expect(child.$submitted).not.toBeTruthy(); + expect(grandchild.$submitted).not.toBeTruthy(); + + // Test upwards submission setting + grandchild.$setSubmitted(); + expect(parent.$submitted).not.toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + expect(grandchild.$submitted).toBeTruthy(); + }); + + + it('should set $submitted to true on child and parent forms when form is submitted', function() { + doc = jqLite( + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''); + $compile(doc)(scope); + + var parent = scope.parent, + child = scope.child, + grandchild = scope.grandchild; + + child.$setSubmitted(); + + expect(parent.$submitted).toBeTruthy(); + expect(child.$submitted).toBeTruthy(); + expect(grandchild.$submitted).toBeTruthy(); + }); it('should deregister a child form when its DOM is removed', function() { doc = jqLite( @@ -463,7 +699,7 @@ describe('form', function() { doc = jqLite( '
' + '
' + - '' + + '' + '
' + '
'); $compile(doc)(scope); @@ -476,54 +712,224 @@ describe('form', function() { expect(parent).toBeDefined(); expect(child).toBeDefined(); + expect(parent.$error.required).toEqual([child]); + expect(parent.$$success.maxlength).toEqual([child]); + expect(child.$error.required).toEqual([input]); + expect(child.$$success.maxlength).toEqual([input]); + expect(doc.hasClass('ng-invalid')).toBe(true); expect(doc.hasClass('ng-invalid-required')).toBe(true); + expect(doc.hasClass('ng-valid-maxlength')).toBe(true); expect(doc.find('div').hasClass('ng-invalid')).toBe(true); expect(doc.find('div').hasClass('ng-invalid-required')).toBe(true); + expect(doc.find('div').hasClass('ng-valid-maxlength')).toBe(true); //remove child input - scope.inputPresent = false; - scope.$apply(); + scope.$apply('inputPresent = false'); expect(parent.$error.required).toBeFalsy(); + expect(parent.$$success.maxlength).toBeFalsy(); + expect(child.$error.required).toBeFalsy(); + expect(child.$$success.maxlength).toBeFalsy(); + expect(doc.hasClass('ng-valid')).toBe(true); expect(doc.hasClass('ng-valid-required')).toBe(false); + expect(doc.hasClass('ng-invalid-required')).toBe(false); + expect(doc.hasClass('ng-valid-maxlength')).toBe(false); + expect(doc.hasClass('ng-invalid-maxlength')).toBe(false); + expect(doc.find('div').hasClass('ng-valid')).toBe(true); expect(doc.find('div').hasClass('ng-valid-required')).toBe(false); + expect(doc.find('div').hasClass('ng-invalid-required')).toBe(false); + expect(doc.find('div').hasClass('ng-valid-maxlength')).toBe(false); + expect(doc.find('div').hasClass('ng-invalid-maxlength')).toBe(false); }); - it('should leave the parent form invalid when deregister a removed input', function() { - doc = jqLite( - '
' + - '
' + - '' + - '' + - '
' + - '
'); - $compile(doc)(scope); - scope.inputPresent = true; - scope.$apply(); + it('should deregister a input that is $pending when it is removed from DOM', function() { + doc = jqLite( + '
' + + '
' + + '' + + '
' + + '
'); + $compile(doc)(scope); + scope.$apply('inputPresent = true'); - var parent = scope.parent, - child = scope.child, - inputA = child.inputA, - inputB = child.inputB; + var parent = scope.parent; + var child = scope.child; + var input = child.inputA; - expect(parent).toBeDefined(); - expect(child).toBeDefined(); - expect(parent.$error.required).toEqual([child]); - expect(child.$error.required).toEqual([inputB, inputA]); + scope.$apply(child.inputA.$setValidity('fake', undefined)); - //remove child input - scope.inputPresent = false; - scope.$apply(); + expect(parent).toBeDefined(); + expect(child).toBeDefined(); + + expect(parent.$pending.fake).toEqual([child]); + expect(child.$pending.fake).toEqual([input]); + + expect(doc.hasClass('ng-pending')).toBe(true); + expect(doc.find('div').hasClass('ng-pending')).toBe(true); + + //remove child input + scope.$apply('inputPresent = false'); + + expect(parent.$pending).toBeUndefined(); + expect(child.$pending).toBeUndefined(); + + expect(doc.hasClass('ng-pending')).toBe(false); + expect(doc.find('div').hasClass('ng-pending')).toBe(false); + }); + + + it('should leave the parent form invalid when deregister a removed input', function() { + doc = jqLite( + '
' + + '
' + + '' + + '' + + '
' + + '
'); + $compile(doc)(scope); + scope.inputPresent = true; + scope.$apply(); + + var parent = scope.parent, + child = scope.child, + inputA = child.inputA, + inputB = child.inputB; + + expect(parent).toBeDefined(); + expect(child).toBeDefined(); + expect(parent.$error.required).toEqual([child]); + expect(child.$error.required).toEqual([inputB, inputA]); + + //remove child input + scope.inputPresent = false; + scope.$apply(); + + expect(parent.$error.required).toEqual([child]); + expect(child.$error.required).toEqual([inputB]); + }); + + + it('should ignore changes in manually removed child forms', function() { + doc = $compile( + '
' + + '' + + '' + + '' + + '
')(scope); + + var form = scope.myForm; + var childformController = doc.find('ng-form').eq(0).controller('form'); + + var input = doc.find('input').eq(0); + var inputController = input.controller('ngModel'); + + changeInputValue(input, 'ab'); + scope.$apply(); + + expect(form.$dirty).toBe(true); + expect(form.$error.maxlength).toBeTruthy(); + expect(form.$error.maxlength[0].$name).toBe('childform'); + + inputController.$setPristine(); + expect(form.$dirty).toBe(true); + + form.$setPristine(); + + // remove child form + form.$removeControl(childformController); + expect(form.childform).toBeUndefined(); + expect(form.$error.maxlength).toBeFalsy(); + + changeInputValue(input, 'abc'); + scope.$apply(); + + expect(form.$error.maxlength).toBeFalsy(); + expect(form.$dirty).toBe(false); + }); + + + it('should react to changes in manually added child forms', function() { + doc = $compile( + '
' + + '' + + '' + + '' + + '
')(scope); + + var form = scope.myForm; + var childFormController = doc.find('ng-form').eq(0).controller('form'); + + var input = doc.find('input').eq(0); + + // remove child form so we can add it manually + form.$removeControl(childFormController); + changeInputValue(input, 'ab'); + + expect(form.childForm).toBeUndefined(); + expect(form.$dirty).toBe(false); + expect(form.$error.maxlength).toBeFalsy(); + + // re-add the child form; its current validation state is not propagated + form.$addControl(childFormController); + expect(form.childForm).toBe(childFormController); + expect(form.$error.maxlength).toBeFalsy(); + expect(form.$dirty).toBe(false); + + // Only when the input inside the child form changes, the validation state is propagated + changeInputValue(input, 'abc'); + expect(form.$error.maxlength[0]).toBe(childFormController); + expect(form.$dirty).toBe(false); + }); + + + it('should use the correct parent when renaming and removing dynamically added forms', function() { + scope.formName = 'childForm'; + scope.hasChildForm = true; + + doc = $compile( + '
' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + '
')(scope); + + scope.$digest(); + + var form = scope.myForm; + var otherForm = scope.otherForm; + var childForm = form.childForm; + + // remove child form and add it to another form + form.$removeControl(childForm); + otherForm.$addControl(childForm); + + expect(form.childForm).toBeUndefined(); + expect(otherForm.childForm).toBe(childForm); + + // rename the childForm + scope.formName = 'childFormMoved'; + scope.$digest(); + + expect(form.childFormMoved).toBeUndefined(); + expect(otherForm.childForm).toBeUndefined(); + expect(otherForm.childFormMoved).toBe(childForm); + + scope.hasChildForm = false; + scope.$digest(); + + expect(form.childFormMoved).toBeUndefined(); + expect(otherForm.childFormMoved).toBeUndefined(); + }); - expect(parent.$error.required).toEqual([child]); - expect(child.$error.required).toEqual([inputB]); - }); it('should chain nested forms in repeater', function() { doc = jqLite( @@ -602,7 +1008,7 @@ describe('form', function() { expect(doc.hasClass('ng-valid-another')).toBe(true); expect(doc.hasClass('ng-invalid-another')).toBe(false); - // validators are skipped, e.g. becuase of a parser error + // validators are skipped, e.g. because of a parser error control.$setValidity('error', null); control.$setValidity('another', null); scope.$digest(); @@ -750,6 +1156,7 @@ describe('form', function() { scope.$digest(); expect(form).toBePristine(); scope.$digest(); + expect(formCtrl.$pristine).toBe(true); expect(formCtrl.$dirty).toBe(false); expect(nestedForm).toBePristine(); @@ -793,6 +1200,52 @@ describe('form', function() { }); }); + describe('$getControls', function() { + it('should return an empty array if the controller has no controls', function() { + doc = $compile('
')(scope); + + scope.$digest(); + + var formCtrl = scope.testForm; + + expect(formCtrl.$getControls()).toEqual([]); + }); + + it('should return a shallow copy of the form controls', function() { + doc = $compile( + '
' + + '' + + '
' + + '' + + '
' + + '
')(scope); + + scope.$digest(); + + var form = doc, + formCtrl = scope.testForm, + formInput = form.children('input').eq(0), + formInputCtrl = formInput.controller('ngModel'), + nestedForm = form.find('div'), + nestedFormCtrl = nestedForm.controller('form'), + nestedInput = nestedForm.children('input').eq(0), + nestedInputCtrl = nestedInput.controller('ngModel'); + + var controls = formCtrl.$getControls(); + + expect(controls).not.toBe(formCtrl.$$controls); + + controls.push('something'); + expect(formCtrl.$$controls).not.toContain('something'); + + expect(controls[0]).toBe(formInputCtrl); + expect(controls[1]).toBe(nestedFormCtrl); + + var nestedControls = controls[1].$getControls(); + + expect(nestedControls[0]).toBe(nestedInputCtrl); + }); + }); it('should rename nested form controls when interpolated name changes', function() { scope.idA = 'A'; @@ -804,7 +1257,7 @@ describe('form', function() { '
' + '
' + - '' )(scope); scope.$digest(); @@ -829,20 +1282,38 @@ describe('form', function() { it('should rename forms with no parent when interpolated name changes', function() { var element = $compile('
')(scope); - var element2 = $compile('
')(scope); - scope.nameID = "A"; + var element2 = $compile('
')(scope); + scope.nameID = 'A'; scope.$digest(); var form = element.controller('form'); var form2 = element2.controller('form'); + expect(scope.nameA).toBe(form); + expect(scope.ngformA).toBe(form2); expect(form.$name).toBe('nameA'); - expect(form2.$name).toBe('nameA'); + expect(form2.$name).toBe('ngformA'); - scope.nameID = "B"; + scope.nameID = 'B'; scope.$digest(); + expect(scope.nameA).toBeUndefined(); + expect(scope.ngformA).toBeUndefined(); + expect(scope.nameB).toBe(form); + expect(scope.ngformB).toBe(form2); expect(form.$name).toBe('nameB'); - expect(form2.$name).toBe('nameB'); + expect(form2.$name).toBe('ngformB'); }); + it('should rename forms with an initially blank name', function() { + var element = $compile('
')(scope); + scope.$digest(); + var form = element.controller('form'); + expect(scope['']).toBe(form); + expect(form.$name).toBe(''); + scope.name = 'foo'; + scope.$digest(); + expect(scope.foo).toBe(form); + expect(form.$name).toBe('foo'); + expect(scope.foo).toBe(form); + }); describe('$setSubmitted', function() { beforeEach(function() { diff --git a/test/ng/directive/inputSpec.js b/test/ng/directive/inputSpec.js index 73d995079421..34ae2e127734 100644 --- a/test/ng/directive/inputSpec.js +++ b/test/ng/directive/inputSpec.js @@ -1,48 +1,49 @@ 'use strict'; -/* globals getInputCompileHelper: false */ +/* globals generateInputCompilerHelper: false */ describe('input', function() { - var helper, $compile, $rootScope, $browser, $sniffer, $timeout, $q; + var helper = {}, $compile, $rootScope, $browser, $sniffer; - beforeEach(function() { - helper = getInputCompileHelper(this); - }); - - afterEach(function() { - helper.dealoc(); - }); + // UA sniffing to exclude Edge from some date input tests + var isEdge = /\bEdge\//.test(window.navigator.userAgent); + generateInputCompilerHelper(helper); - beforeEach(inject(function(_$compile_, _$rootScope_, _$browser_, _$sniffer_, _$timeout_, _$q_) { + beforeEach(inject(function(_$compile_, _$rootScope_, _$browser_, _$sniffer_) { $compile = _$compile_; $rootScope = _$rootScope_; $browser = _$browser_; $sniffer = _$sniffer_; - $timeout = _$timeout_; - $q = _$q_; })); it('should bind to a model', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("name = 'misko'"); + $rootScope.$apply('name = \'misko\''); expect(inputElm.val()).toBe('misko'); }); it('should not set readonly or disabled property on ie7', function() { - this.addMatchers({ - toBeOff: function(attributeName) { - var actualValue = this.actual.attr(attributeName); - this.message = function() { - return "Attribute '" + attributeName + "' expected to be off but was '" + actualValue + - "' in: " + angular.mock.dump(this.actual); + jasmine.addMatchers({ + toBeOff: function() { + return { + compare: function(actual, attributeName) { + var actualValue = actual.attr(attributeName); + var message = function() { + return 'Attribute \'' + attributeName + '\' expected to be off but was \'' + actualValue + + '\' in: ' + angular.mock.dump(actual); + }; + + return { + pass: !actualValue || actualValue === 'false', + message: message + }; + } }; - - return !actualValue || actualValue == 'false'; } }); @@ -72,6 +73,25 @@ describe('input', function() { expect($rootScope.form.$$renameControl).not.toHaveBeenCalled(); }); + + it('should not set the `val` property when the value is equal to the current value', inject(function($rootScope, $compile) { + // This is a workaround for Firefox validation. Look at #12102. + var input = jqLite(''); + var setterCalls = 0; + $rootScope.foo = ''; + Object.defineProperty(input[0], 'value', { + get: function() { + return ''; + }, + set: function() { + setterCalls++; + } + }); + $compile(input)($rootScope); + $rootScope.$digest(); + expect(setterCalls).toBe(0); + })); + describe('compositionevents', function() { it('should not update the model between "compositionstart" and "compositionend" on non android', function() { @@ -112,37 +132,60 @@ describe('input', function() { browserTrigger(inputElm, 'compositionend'); expect($rootScope.name).toEqual('caitp'); }); + + + it('should end composition on "compositionupdate" when event.data is ""', function() { + // This tests a bug workaround for IE9-11 + // During composition, when an input is de-focussed by clicking away from it, + // the compositionupdate event is called with '', followed by a change event. + var inputElm = helper.compileInput(''); + browserTrigger(inputElm, 'compositionstart'); + helper.changeInputValueTo('caitp'); + expect($rootScope.name).toBeUndefined(); + browserTrigger(inputElm, 'compositionupdate', {data: ''}); + browserTrigger(inputElm, 'change'); + expect($rootScope.name).toEqual('caitp'); + }); }); - describe("IE placeholder input events", function() { + describe('IE placeholder input events', function() { + // Support: IE 9-11 only //IE fires an input event whenever a placeholder visually changes, essentially treating it as a value //Events: // placeholder attribute change: *input* // focus (which visually removes the placeholder value): focusin focus *input* // blur (which visually creates the placeholder value): focusout *input* blur //However none of these occur if the placeholder is not visible at the time of the event. - //These tests try simulate various scenerios which do/do-not fire the extra input event + //These tests try simulate various scenarios which do/do-not fire the extra input event it('should not dirty the model on an input event in response to a placeholder change', function() { var inputElm = helper.compileInput(''); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe('Test'); expect(inputElm).toBePristine(); helper.attrs.$set('placeholder', ''); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe(''); expect(inputElm).toBePristine(); helper.attrs.$set('placeholder', 'Test Again'); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe('Test Again'); expect(inputElm).toBePristine(); helper.attrs.$set('placeholder', undefined); - msie && browserTrigger(inputElm, 'input'); - expect(inputElm.attr('placeholder')).toBe(undefined); + if (msie) { + browserTrigger(inputElm, 'input'); + } + expect(inputElm.attr('placeholder')).toBeUndefined(); expect(inputElm).toBePristine(); helper.changeInputValueTo('foo'); @@ -152,17 +195,23 @@ describe('input', function() { it('should not dirty the model on an input event in response to a interpolated placeholder change', function() { var inputElm = helper.compileInput(''); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm).toBePristine(); $rootScope.ph = 1; $rootScope.$digest(); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm).toBePristine(); - $rootScope.ph = ""; + $rootScope.ph = ''; $rootScope.$digest(); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm).toBePristine(); helper.changeInputValueTo('foo'); @@ -177,7 +226,9 @@ describe('input', function() { browserTrigger(inputElm, 'focusin'); browserTrigger(inputElm, 'focus'); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe('Test'); expect(inputElm).toBePristine(); @@ -196,12 +247,16 @@ describe('input', function() { $rootScope.ph = 1; $rootScope.$digest(); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm).toBePristine(); - $rootScope.ph = ""; + $rootScope.ph = ''; $rootScope.$digest(); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm).toBePristine(); helper.changeInputValueTo('foo'); @@ -211,13 +266,17 @@ describe('input', function() { it('should not dirty the model on an input event in response to a focus', function() { var inputElm = helper.compileInput(''); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe('Test'); expect(inputElm).toBePristine(); browserTrigger(inputElm, 'focusin'); browserTrigger(inputElm, 'focus'); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe('Test'); expect(inputElm).toBePristine(); @@ -228,17 +287,23 @@ describe('input', function() { it('should not dirty the model on an input event in response to a blur', function() { var inputElm = helper.compileInput(''); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm.attr('placeholder')).toBe('Test'); expect(inputElm).toBePristine(); browserTrigger(inputElm, 'focusin'); browserTrigger(inputElm, 'focus'); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } expect(inputElm).toBePristine(); browserTrigger(inputElm, 'focusout'); - msie && browserTrigger(inputElm, 'input'); + if (msie) { + browserTrigger(inputElm, 'input'); + } browserTrigger(inputElm, 'blur'); expect(inputElm).toBePristine(); @@ -301,11 +366,11 @@ describe('input', function() { it('should rename form controls in form when interpolated name changes', function() { - $rootScope.nameID = "A"; + $rootScope.nameID = 'A'; var inputElm = helper.compileInput(''); expect($rootScope.form.nameA.$name).toBe('nameA'); var oldModel = $rootScope.form.nameA; - $rootScope.nameID = "B"; + $rootScope.nameID = 'B'; $rootScope.$digest(); expect($rootScope.form.nameA).toBeUndefined(); expect($rootScope.form.nameB).toBe(oldModel); @@ -314,12 +379,12 @@ describe('input', function() { it('should rename form controls in null form when interpolated name changes', function() { - $rootScope.nameID = "A"; + $rootScope.nameID = 'A'; var inputElm = helper.compileInput(''); var model = inputElm.controller('ngModel'); expect(model.$name).toBe('nameA'); - $rootScope.nameID = "B"; + $rootScope.nameID = 'B'; $rootScope.$digest(); expect(model.$name).toBe('nameB'); }); @@ -369,8 +434,7 @@ describe('input', function() { scope.field = 'fake field'; scope.$watch('field', function() { - // We need to use _originalTrigger since trigger is modified by Angular Scenario. - inputElm._originalTrigger('change'); + inputElm.trigger('change'); }); scope.$apply(); }; @@ -387,7 +451,7 @@ describe('input', function() { } }); - describe('"keydown", "paste" and "cut" events', function() { + describe('"keydown", "paste", "cut" and "drop" events', function() { beforeEach(function() { // Force browser to report a lack of an 'input' event $sniffer.hasEvent = function(eventName) { @@ -409,6 +473,18 @@ describe('input', function() { expect($rootScope.name).toEqual('mark'); }); + it('should update the model on "drop" event if the input value changes', function() { + var inputElm = helper.compileInput(''); + + browserTrigger(inputElm, 'keydown'); + $browser.defer.flush(); + expect(inputElm).toBePristine(); + + inputElm.val('mark'); + browserTrigger(inputElm, 'drop'); + $browser.defer.flush(); + expect($rootScope.name).toEqual('mark'); + }); it('should update the model on "cut" event', function() { var inputElm = helper.compileInput(''); @@ -460,7 +536,7 @@ describe('input', function() { it('should allow complex reference binding', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("obj = { abc: { name: 'Misko'} }"); + $rootScope.$apply('obj = { abc: { name: \'Misko\'} }'); expect(inputElm.val()).toEqual('Misko'); }); @@ -479,11 +555,11 @@ describe('input', function() { it('should report error on assignment error', function() { expect(function() { var inputElm = helper.compileInput(''); - }).toThrowMinErr("$parse", "syntax", "Syntax Error: Token '''' is an unexpected token at column 7 of the expression [throw ''] starting at ['']."); + }).toThrowMinErr('$parse', 'syntax', 'Syntax Error: Token \'\'\'\' is an unexpected token at column 7 of the expression [throw \'\'] starting at [\'\'].'); }); - it("should render as blank if null", function() { + it('should render as blank if null', function() { var inputElm = helper.compileInput(''); $rootScope.$apply('age = null'); @@ -551,16 +627,43 @@ describe('input', function() { expect(inputElm.val()).toBe('2013-01'); - try { - //set to text for browsers with datetime-local validation. - inputElm[0].setAttribute('type', 'text'); - } catch (e) { - //for IE8 - } + //set to text for browsers with datetime-local validation. + inputElm[0].setAttribute('type', 'text'); helper.changeInputValueTo('stuff'); expect(inputElm.val()).toBe('stuff'); expect($rootScope.value).toBeUndefined(); + expect(inputElm).toHaveClass('ng-invalid-month'); + expect(inputElm).toBeInvalid(); + }); + + + it('should not set error=month when a later parser returns undefined', function() { + var inputElm = helper.compileInput(''); + var ctrl = inputElm.controller('ngModel'); + + ctrl.$parsers.push(function() { + return undefined; + }); + + inputElm[0].setAttribute('type', 'text'); + + helper.changeInputValueTo('2017-01'); + + expect($rootScope.value).toBeUndefined(); + expect(ctrl.$error.month).toBeFalsy(); + expect(ctrl.$error.parse).toBeTruthy(); + expect(inputElm).not.toHaveClass('ng-invalid-month'); + expect(inputElm).toHaveClass('ng-invalid-parse'); + expect(inputElm).toBeInvalid(); + + helper.changeInputValueTo('asdf'); + + expect($rootScope.value).toBeUndefined(); + expect(ctrl.$error.month).toBeTruthy(); + expect(ctrl.$error.parse).toBeFalsy(); + expect(inputElm).toHaveClass('ng-invalid-month'); + expect(inputElm).not.toHaveClass('ng-invalid-parse'); expect(inputElm).toBeInvalid(); }); @@ -613,6 +716,39 @@ describe('input', function() { }); + it('should be possible to override the timezone', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('2013-07'); + expect(+$rootScope.value).toBe(Date.UTC(2013, 6, 1)); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'}); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2013, 6, 1)); + }); + expect(inputElm.val()).toBe('2013-06'); + }); + + + they('should use any timezone if specified in the options (format: $prop)', + {'+HHmm': '+0500', '+HH:mm': '+05:00'}, + function(tz) { + var ngModelOptions = '{timezone: \'' + tz + '\'}'; + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('2013-07'); + expect(+$rootScope.value).toBe(Date.UTC(2013, 5, 30, 19, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2014, 5, 30, 19, 0, 0)); + }); + expect(inputElm.val()).toBe('2014-07'); + } + ); + + it('should label parse errors as `month`', function() { var inputElm = helper.compileInput('', { valid: false, @@ -625,6 +761,22 @@ describe('input', function() { }); + // Support: Edge 16 + // Edge does not support years with any number of digits other than 4. + if (!isEdge) { + it('should allow four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-03'); + expect(+$rootScope.value).toBe(Date.UTC(10123, 2, 1, 0, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(20456, 3, 1, 0, 0, 0)); + }); + expect(inputElm.val()).toBe('20456-04'); + }); + } + it('should only change the month of a bound date', function() { var inputElm = helper.compileInput(''); @@ -636,6 +788,17 @@ describe('input', function() { expect(inputElm.val()).toBe('2013-12'); }); + it('should only change the month of a bound date in any timezone', function() { + var inputElm = helper.compileInput(''); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2013, 6, 31, 20, 0, 0)); + }); + helper.changeInputValueTo('2013-09'); + expect(+$rootScope.value).toBe(Date.UTC(2013, 7, 31, 20, 0, 0)); + expect(inputElm.val()).toBe('2013-09'); + }); + describe('min', function() { var inputElm; beforeEach(function() { @@ -668,6 +831,30 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); }); describe('max', function() { @@ -702,6 +889,47 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate when timezone is provided.', function() { + inputElm = helper.compileInput(''); + $rootScope.maxVal = '2013-01'; + $rootScope.value = new Date(Date.UTC(2013, 0, 1, 0, 0, 0)); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + + $rootScope.value = ''; + helper.changeInputValueTo('2013-01'); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); }); }); @@ -752,12 +980,8 @@ describe('input', function() { expect(inputElm.val()).toBe('2013-W02'); - try { - //set to text for browsers with datetime-local validation. - inputElm[0].setAttribute('type', 'text'); - } catch (e) { - //for IE8 - } + //set to text for browsers with datetime-local validation. + inputElm[0].setAttribute('type', 'text'); helper.changeInputValueTo('stuff'); expect(inputElm.val()).toBe('stuff'); @@ -800,6 +1024,21 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + // Support: Edge 16 + // Edge does not support years with any number of digits other than 4. + if (!isEdge) { + it('should allow four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-W03'); + expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 21)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(20456, 0, 28)); + }); + expect(inputElm.val()).toBe('20456-W04'); + }); + } it('should use UTC if specified in the options', function() { var inputElm = helper.compileInput(''); @@ -814,6 +1053,48 @@ describe('input', function() { }); + it('should be possible to override the timezone', function() { + var inputElm = helper.compileInput(''); + + // January 19 2013 is a Saturday + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2013, 0, 19)); + }); + + expect(inputElm.val()).toBe('2013-W03'); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: '+2400'}); + + // To check that the timezone overwrite works, apply an offset of +24 hours. + // Since January 19 is a Saturday, +24 will turn the formatted Date into January 20 - Sunday - + // which is in calendar week 4 instead of 3. + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2013, 0, 19)); + }); + + // Verifying that the displayed week is week 4 confirms that overriding the timezone worked + expect(inputElm.val()).toBe('2013-W04'); + }); + + + they('should use any timezone if specified in the options (format: $prop)', + {'+HHmm': '+0500', '+HH:mm': '+05:00'}, + function(tz) { + var ngModelOptions = '{timezone: \'' + tz + '\'}'; + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('2013-W03'); + expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 16, 19, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2014, 0, 16, 19, 0, 0)); + }); + expect(inputElm.val()).toBe('2014-W03'); + } + ); + + it('should label parse errors as `week`', function() { var inputElm = helper.compileInput('', { valid: false, @@ -857,6 +1138,30 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); }); describe('max', function() { @@ -892,6 +1197,49 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate when timezone is provided.', function() { + inputElm = helper.compileInput(''); + // The calendar week comparison date is January 17. Setting the timezone to -2400 + // makes the January 18 date value valid. + $rootScope.maxVal = '2013-W03'; + $rootScope.value = new Date(Date.UTC(2013, 0, 18)); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + + $rootScope.value = ''; + helper.changeInputValueTo('2013-W03'); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); }); }); @@ -923,17 +1271,13 @@ describe('input', function() { var inputElm = helper.compileInput(''); $rootScope.$apply(function() { - $rootScope.breakMe = new Date(2009, 0, 6, 16, 25, 0); + $rootScope.breakMe = new Date(2009, 0, 6, 16, 25, 1, 337); }); - expect(inputElm.val()).toBe('2009-01-06T16:25:00.000'); + expect(inputElm.val()).toBe('2009-01-06T16:25:01.337'); - try { - //set to text for browsers with datetime-local validation. - inputElm[0].setAttribute('type', 'text'); - } catch (e) { - //for IE8 - } + //set to text for browsers with datetime-local validation. + inputElm[0].setAttribute('type', 'text'); helper.changeInputValueTo('stuff'); expect(inputElm.val()).toBe('stuff'); @@ -980,13 +1324,61 @@ describe('input', function() { it('should use UTC if specified in the options', function() { var inputElm = helper.compileInput(''); - helper.changeInputValueTo('2000-01-01T01:02'); - expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 0)); + helper.changeInputValueTo('2000-01-01T01:02:03.456'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 3, 456)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 3, 456)); + }); + expect(inputElm.val()).toBe('2001-01-01T01:02:03.456'); + }); + + it('should be possible to override the timezone', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('2000-01-01T01:02:03.456'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 3, 456)); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: '+0500'}); $rootScope.$apply(function() { - $rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 0)); + $rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 3, 456)); }); - expect(inputElm.val()).toBe('2001-01-01T01:02:00.000'); + expect(inputElm.val()).toBe('2001-01-01T06:02:03.456'); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'}); + + helper.changeInputValueTo('2000-01-01T01:02:03.456'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 3, 456)); + }); + + + they('should use any timezone if specified in the options (format: $prop)', + {'+HHmm': '+0500', '+HH:mm': '+05:00'}, + function(tz) { + var ngModelOptions = '{timezone: \'' + tz + '\'}'; + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('2000-01-01T06:02:03.456'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 1, 2, 3, 456)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2001, 0, 1, 1, 2, 3, 456)); + }); + expect(inputElm.val()).toBe('2001-01-01T06:02:03.456'); + } + ); + + + it('should fallback to default timezone in case an unknown timezone was passed', function() { + var inputElm = helper.compileInput( + '' + + ''); + + helper.changeGivenInputTo(inputElm.eq(0), '2000-01-01T06:02'); + helper.changeGivenInputTo(inputElm.eq(1), '2000-01-01T06:02'); + expect($rootScope.value1).toEqual($rootScope.value2); }); @@ -1009,13 +1401,13 @@ describe('input', function() { it('should allow to specify the seconds', function() { var inputElm = helper.compileInput(''); - helper.changeInputValueTo('2000-01-01T01:02:03'); - expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 1, 2, 3)); + helper.changeInputValueTo('2000-01-01T01:02:03.456'); + expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 1, 2, 3, 456)); $rootScope.$apply(function() { - $rootScope.value = new Date(2001, 0, 1, 1, 2, 3); + $rootScope.value = new Date(2001, 0, 1, 1, 2, 3, 456); }); - expect(inputElm.val()).toBe('2001-01-01T01:02:03.000'); + expect(inputElm.val()).toBe('2001-01-01T01:02:03.456'); }); @@ -1027,6 +1419,24 @@ describe('input', function() { }); + // Support: Edge 16 + // Edge does not support years with any number of digits other than 4. + if (!isEdge) { + it('should allow four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-01-01T01:02:03.456'); + expect(+$rootScope.value).toBe(+new Date(10123, 0, 1, 1, 2, 3, 456)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(20456, 1, 1, 1, 2, 3, 456); + }); + expect(inputElm.val()).toBe('20456-02-01T01:02:03.456'); + } + ); + } + + it('should label parse errors as `datetimelocal`', function() { var inputElm = helper.compileInput('', { valid: false, @@ -1038,45 +1448,152 @@ describe('input', function() { expect($rootScope.form.alias.$error.datetimelocal).toBeTruthy(); }); - describe('min', function() { - var inputElm; - beforeEach(function() { - $rootScope.minVal = '2000-01-01T12:30:00'; - inputElm = helper.compileInput(''); - }); + it('should use the timeSecondsFormat specified in ngModelOptions', function() { + var inputElm = helper.compileInput( + '' + ); - it('should invalidate', function() { - helper.changeInputValueTo('1999-12-31T01:02:00'); - expect(inputElm).toBeInvalid(); - expect($rootScope.value).toBeFalsy(); - expect($rootScope.form.alias.$error.min).toBeTruthy(); + var ctrl = inputElm.controller('ngModel'); + + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 0, 500); }); + expect(inputElm.val()).toBe('1970-01-01T15:41'); - it('should validate', function() { - helper.changeInputValueTo('2000-01-01T23:02:00'); - expect(inputElm).toBeValid(); - expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 23, 2, 0)); - expect($rootScope.form.alias.$error.min).toBeFalsy(); + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 50, 500); }); + expect(inputElm.val()).toBe('1970-01-01T15:41'); - it('should revalidate when the min value changes', function() { - helper.changeInputValueTo('2000-02-01T01:02:00'); - expect(inputElm).toBeValid(); - expect($rootScope.form.alias.$error.min).toBeFalsy(); + ctrl.$overrideModelOptions({timeSecondsFormat: 'ss'}); - $rootScope.minVal = '2010-01-01T01:02:00'; - $rootScope.$digest(); + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 5, 500); + }); + expect(inputElm.val()).toBe('1970-01-01T15:41:05'); - expect(inputElm).toBeInvalid(); - expect($rootScope.form.alias.$error.min).toBeTruthy(); + ctrl.$overrideModelOptions({timeSecondsFormat: 'ss.sss'}); + + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 50, 50); }); + expect(inputElm.val()).toBe('1970-01-01T15:41:50.050'); }); - describe('max', function() { - var inputElm; - beforeEach(function() { - $rootScope.maxVal = '2019-01-01T01:02:00'; - inputElm = helper.compileInput(''); + + it('should strip empty milliseconds and seconds if specified in ngModelOptions', function() { + var inputElm = helper.compileInput( + '' + ); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 50, 500); + }); + + expect(inputElm.val()).toBe('1970-01-01T15:41:50.500'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 0, 500); + }); + + expect(inputElm.val()).toBe('1970-01-01T15:41:00.500'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 50, 0); + }); + + expect(inputElm.val()).toBe('1970-01-01T15:41:50'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 0, 0); + }); + + expect(inputElm.val()).toBe('1970-01-01T15:41'); + }); + + + it('should apply timeStripZeroSeconds after timeSecondsFormat', function() { + var inputElm = helper.compileInput(''); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 50, 500); + }); + + expect(inputElm.val()).toBe('1970-01-01T15:41:50'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 0, 500); + }); + + expect(inputElm.val()).toBe('1970-01-01T15:41'); + }); + + describe('min', function() { + var inputElm; + beforeEach(function() { + $rootScope.minVal = '2000-01-01T12:30:00'; + inputElm = helper.compileInput(''); + }); + + it('should invalidate', function() { + helper.changeInputValueTo('1999-12-31T01:02:00'); + expect(inputElm).toBeInvalid(); + expect($rootScope.value).toBeFalsy(); + expect($rootScope.form.alias.$error.min).toBeTruthy(); + }); + + it('should validate', function() { + helper.changeInputValueTo('2000-01-01T23:02:00'); + expect(inputElm).toBeValid(); + expect(+$rootScope.value).toBe(+new Date(2000, 0, 1, 23, 2, 0)); + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); + + it('should revalidate when the min value changes', function() { + helper.changeInputValueTo('2000-02-01T01:02:00'); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$error.min).toBeFalsy(); + + $rootScope.minVal = '2010-01-01T01:02:00'; + $rootScope.$digest(); + + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$error.min).toBeTruthy(); + }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); + + }); + + describe('max', function() { + var inputElm; + beforeEach(function() { + $rootScope.maxVal = '2019-01-01T01:02:00'; + inputElm = helper.compileInput(''); }); it('should invalidate', function() { @@ -1104,6 +1621,47 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(3000, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate when timezone is provided.', function() { + inputElm = helper.compileInput(''); + $rootScope.maxVal = '2013-01-01T00:00:00'; + $rootScope.value = new Date(Date.UTC(2013, 0, 1, 0, 0, 0)); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + + $rootScope.value = ''; + helper.changeInputValueTo('2013-01-01T00:00:00'); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); }); @@ -1181,6 +1739,24 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + + + // Support: Edge 16 + // Edge does not support years with any number of digits other than 4. + if (!isEdge) { + it('should correctly handle 2-digit years', function() { + helper.compileInput(''); + + helper.changeInputValueTo('0001-01-01T12:34:00'); + expect($rootScope.value.getFullYear()).toBe(1); + + helper.changeInputValueTo('0099-01-01T12:34:00'); + expect($rootScope.value.getFullYear()).toBe(99); + + helper.changeInputValueTo('0100-01-01T12:34:00'); + expect($rootScope.value.getFullYear()).toBe(100); + }); + } }); @@ -1196,7 +1772,7 @@ describe('input', function() { }); - it('should set the view if the model if a valid Date object.', function() { + it('should set the view if the model is a valid Date object.', function() { var inputElm = helper.compileInput(''); $rootScope.$apply(function() { @@ -1207,7 +1783,7 @@ describe('input', function() { }); - it('should set the model undefined if the view is invalid', function() { + it('should set the model to undefined if the view is invalid', function() { var inputElm = helper.compileInput(''); $rootScope.$apply(function() { @@ -1216,12 +1792,8 @@ describe('input', function() { expect(inputElm.val()).toBe('16:25:00.000'); - try { - //set to text for browsers with time validation. - inputElm[0].setAttribute('type', 'text'); - } catch (e) { - //for IE8 - } + //set to text for browsers with time validation. + inputElm[0].setAttribute('type', 'text'); helper.changeInputValueTo('stuff'); expect(inputElm.val()).toBe('stuff'); @@ -1230,7 +1802,7 @@ describe('input', function() { }); - it('should render as blank if null', function() { + it('should set blank if null', function() { var inputElm = helper.compileInput(''); $rootScope.$apply('test = null'); @@ -1240,7 +1812,7 @@ describe('input', function() { }); - it('should come up blank when no value specified', function() { + it('should set blank when no value specified', function() { var inputElm = helper.compileInput(''); expect(inputElm.val()).toBe(''); @@ -1251,6 +1823,88 @@ describe('input', function() { expect(inputElm.val()).toBe(''); }); + it('should use the timeSecondsFormat specified in ngModelOptions', function() { + var inputElm = helper.compileInput( + '' + ); + + var ctrl = inputElm.controller('ngModel'); + + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 0, 500); + }); + expect(inputElm.val()).toBe('15:41'); + + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 50, 500); + }); + expect(inputElm.val()).toBe('15:41'); + + ctrl.$overrideModelOptions({timeSecondsFormat: 'ss'}); + + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 5, 500); + }); + expect(inputElm.val()).toBe('15:41:05'); + + ctrl.$overrideModelOptions({timeSecondsFormat: 'ss.sss'}); + + $rootScope.$apply(function() { + $rootScope.time = new Date(1970, 0, 1, 15, 41, 50, 50); + }); + expect(inputElm.val()).toBe('15:41:50.050'); + }); + + + it('should strip empty milliseconds and seconds if specified in ngModelOptions', function() { + var inputElm = helper.compileInput( + '' + ); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 50, 500); + }); + + expect(inputElm.val()).toBe('15:41:50.500'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 0, 500); + }); + + expect(inputElm.val()).toBe('15:41:00.500'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 50, 0); + }); + + expect(inputElm.val()).toBe('15:41:50'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 0, 0); + }); + + expect(inputElm.val()).toBe('15:41'); + }); + + + it('should apply timeStripZeroSeconds after timeSecondsFormat', function() { + var inputElm = helper.compileInput(''); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 50, 500); + }); + + expect(inputElm.val()).toBe('15:41:50'); + + $rootScope.$apply(function() { + $rootScope.threeFortyOnePm = new Date(1970, 0, 1, 15, 41, 0, 500); + }); + + expect(inputElm.val()).toBe('15:41'); + }); + it('should parse empty string to null', function() { var inputElm = helper.compileInput(''); @@ -1278,6 +1932,43 @@ describe('input', function() { }); + it('should be possible to override the timezone', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('23:02:00'); + expect(+$rootScope.value).toBe(Date.UTC(1970, 0, 1, 23, 2, 0)); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'}); + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(1971, 0, 1, 23, 2, 0)); + }); + expect(inputElm.val()).toBe('18:02:00.000'); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'}); + helper.changeInputValueTo('23:02:00'); + // The year is still set from the previous date + expect(+$rootScope.value).toBe(Date.UTC(1971, 0, 1, 23, 2, 0)); + }); + + + they('should use any timezone if specified in the options (format: $prop)', + {'+HHmm': '+0500', '+HH:mm': '+05:00'}, + function(tz) { + var ngModelOptions = '{timezone: \'' + tz + '\'}'; + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('23:02:00'); + expect(+$rootScope.value).toBe(Date.UTC(1970, 0, 1, 18, 2, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(1971, 0, 1, 18, 2, 0)); + }); + expect(inputElm.val()).toBe('23:02:00.000'); + } + ); + + it('should allow to specify the milliseconds', function() { var inputElm = helper.compileInput(''); @@ -1370,12 +2061,37 @@ describe('input', function() { expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.min).toBeTruthy(); }); + + it('should validate if min is empty', function() { + $rootScope.minVal = undefined; + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); }); describe('max', function() { var inputElm; beforeEach(function() { - inputElm = helper.compileInput(''); + $rootScope.maxVal = '22:30:00'; + inputElm = helper.compileInput(''); }); it('should invalidate', function() { @@ -1391,11 +2107,52 @@ describe('input', function() { expect(+$rootScope.value).toBe(+new Date(1970, 0, 1, 5, 30, 0)); expect($rootScope.form.alias.$error.max).toBeFalsy(); }); + + it('should validate if max is empty', function() { + $rootScope.maxVal = undefined; + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate when timezone is provided.', function() { + inputElm = helper.compileInput(''); + $rootScope.maxVal = '22:30:00'; + $rootScope.value = new Date(Date.UTC(1970, 0, 1, 22, 30, 0)); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + + $rootScope.value = ''; + helper.changeInputValueTo('22:30:00'); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); }); it('should validate even if max value changes on-the-fly', function() { - $rootScope.max = '4:02:00'; + $rootScope.max = '04:02:00'; var inputElm = helper.compileInput(''); helper.changeInputValueTo('05:34:00'); @@ -1423,7 +2180,7 @@ describe('input', function() { it('should validate even if ng-max value changes on-the-fly', function() { - $rootScope.max = '4:02:00'; + $rootScope.max = '04:02:00'; var inputElm = helper.compileInput(''); helper.changeInputValueTo('05:34:00'); @@ -1497,12 +2254,8 @@ describe('input', function() { expect(inputElm.val()).toBe('2014-09-14'); - try { - //set to text for browsers with date validation. - inputElm[0].setAttribute('type', 'text'); - } catch (e) { - //for IE8 - } + //set to text for browsers with date validation. + inputElm[0].setAttribute('type', 'text'); helper.changeInputValueTo('1-2-3'); expect(inputElm.val()).toBe('1-2-3'); @@ -1559,6 +2312,56 @@ describe('input', function() { }); + it('should be possible to override the timezone', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('2000-01-01'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1)); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'}); + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2001, 0, 1)); + }); + expect(inputElm.val()).toBe('2000-12-31'); + + inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'}); + helper.changeInputValueTo('2000-01-01'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 0)); + }); + + + they('should use any timezone if specified in the options (format: $prop)', + {'+HHmm': '+0500', '+HH:mm': '+05:00'}, + function(tz) { + var ngModelOptions = '{timezone: \'' + tz + '\'}'; + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('2000-01-01'); + expect(+$rootScope.value).toBe(Date.UTC(1999, 11, 31, 19, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2000, 11, 31, 19, 0, 0)); + }); + expect(inputElm.val()).toBe('2001-01-01'); + } + ); + + if (!isEdge) { + it('should allow four or more digits in year', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10123-01-01'); + expect(+$rootScope.value).toBe(Date.UTC(10123, 0, 1, 0, 0, 0)); + + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(20456, 1, 1, 0, 0, 0)); + }); + expect(inputElm.val()).toBe('20456-02-01'); + } + ); + } + it('should label parse errors as `date`', function() { var inputElm = helper.compileInput('', { valid: false, @@ -1612,6 +2415,34 @@ describe('input', function() { dealoc(formElm); }); + it('should not reuse the hours part of a previous date object after changing the timezone', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('2000-01-01'); + // The Date parser sets the hours part of the Date to 0 (00:00) (UTC) + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 0)); + + // Change the timezone offset so that the display date is a day earlier + // This does not change the model, but our implementation + // internally caches a Date object with this offset + // and re-uses it if part of the Date changes. + // See https://github.com/angular/angular.js/commit/1a1ef62903c8fdf4ceb81277d966a8eff67f0a96 + inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'}); + $rootScope.$apply(function() { + $rootScope.value = new Date(Date.UTC(2000, 0, 1, 0)); + }); + expect(inputElm.val()).toBe('1999-12-31'); + + // At this point, the cached Date has its hours set to to 19 (00:00 - 05:00 = 19:00) + inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'}); + + // When changing the timezone back to UTC, the hours part of the Date should be set to + // the default 0 (UTC) and not use the modified value of the cached Date object. + helper.changeInputValueTo('2000-01-01'); + expect(+$rootScope.value).toBe(Date.UTC(2000, 0, 1, 0)); + }); + + describe('min', function() { it('should invalidate', function() { @@ -1634,16 +2465,56 @@ describe('input', function() { var inputElm = helper.compileInput(''); $rootScope.value = new Date(2010, 1, 1, 0, 0, 0); - $rootScope.min = new Date(2014, 10, 10, 0, 0, 0); + $rootScope.min = new Date(2014, 10, 10, 0, 0, 0).toISOString(); $rootScope.$digest(); expect($rootScope.form.myControl.$error.min).toBeTruthy(); }); + + it('should parse interpolated Date objects as a valid min date value', function() { + var inputElm = helper.compileInput(''); + + $rootScope.value = new Date(2010, 1, 1, 0, 0, 0); + $rootScope.min = new Date(2014, 10, 10, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.myControl.$error.min).toBeTruthy(); + }); + + it('should validate if min is empty', function() { + var inputElm = helper.compileInput( + ''); + + $rootScope.value = new Date(-9999, 0, 1, 0, 0, 0); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.min).toBeFalsy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.minVal = '2000-01-01'; + $rootScope.value = new Date(2010, 1, 1, 0, 0, 0); + + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); + }); - describe('max', function() { + describe('max', function() { - it('should invalidate', function() { + it('should invalidate', function() { var inputElm = helper.compileInput(''); helper.changeInputValueTo('2019-12-31'); expect(inputElm).toBeInvalid(); @@ -1662,12 +2533,69 @@ describe('input', function() { it('should parse ISO-based date strings as a valid max date value', function() { var inputElm = helper.compileInput(''); + $rootScope.value = new Date(2020, 1, 1, 0, 0, 0); + $rootScope.max = new Date(2014, 10, 10, 0, 0, 0).toISOString(); + $rootScope.$digest(); + + expect($rootScope.form.myControl.$error.max).toBeTruthy(); + }); + + it('should parse interpolated Date objects as a valid max date value', function() { + var inputElm = helper.compileInput(''); + $rootScope.value = new Date(2020, 1, 1, 0, 0, 0); $rootScope.max = new Date(2014, 10, 10, 0, 0, 0); $rootScope.$digest(); expect($rootScope.form.myControl.$error.max).toBeTruthy(); }); + + it('should validate if max is empty', function() { + var inputElm = helper.compileInput( + ''); + + $rootScope.value = new Date(9999, 11, 31, 23, 59, 59); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate when timezone is provided.', function() { + var inputElm = helper.compileInput(''); + + $rootScope.maxVal = '2013-12-01'; + $rootScope.value = new Date(Date.UTC(2013, 11, 1, 0, 0, 0)); + $rootScope.$digest(); + + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + + $rootScope.value = ''; + helper.changeInputValueTo('2013-12-01'); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + expect($rootScope.form.alias.$valid).toBeTruthy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.maxVal = '2000-01-01'; + $rootScope.value = new Date(2020, 1, 1, 0, 0, 0); + + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + + inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); }); @@ -1745,23 +2673,275 @@ describe('input', function() { expect(inputElm).toBeValid(); }); + + + it('should allow Date objects as valid ng-max values', function() { + $rootScope.max = new Date(2012, 1, 1, 1, 2, 0); + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('2014-01-01'); + expect(inputElm).toBeInvalid(); + + $rootScope.max = new Date(2013, 1, 1, 1, 2, 0); + $rootScope.$digest(); + + expect(inputElm).toBeInvalid(); + + $rootScope.max = new Date(2014, 1, 1, 1, 2, 0); + $rootScope.$digest(); + + expect(inputElm).toBeValid(); + }); + + + it('should allow Date objects as valid ng-min values', function() { + $rootScope.min = new Date(2013, 1, 1, 1, 2, 0); + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('2010-01-01'); + expect(inputElm).toBeInvalid(); + + $rootScope.min = new Date(2014, 1, 1, 1, 2, 0); + $rootScope.$digest(); + + expect(inputElm).toBeInvalid(); + + $rootScope.min = new Date(2009, 1, 1, 1, 2, 0); + $rootScope.$digest(); + + expect(inputElm).toBeValid(); + }); + + // Support: Edge 16 + // Edge does not support years with any number of digits other than 4. + if (!isEdge) { + it('should correctly handle 2-digit years', function() { + helper.compileInput(''); + + helper.changeInputValueTo('0001-01-01'); + expect($rootScope.value.getFullYear()).toBe(1); + + helper.changeInputValueTo('0099-01-01'); + expect($rootScope.value.getFullYear()).toBe(99); + + helper.changeInputValueTo('0100-01-01'); + expect($rootScope.value.getFullYear()).toBe(100); + }); + } + + + describe('ISO_DATE_REGEXP', function() { + var dates = [ + // Validate date + ['00:00:00.0000+01:01', false], // date must be specified + ['2010.06.15T00:00:00.0000+01:01', false], // date must use dash separator + ['x2010-06-15T00:00:00.0000+01:01', false], // invalid leading characters + + // Validate year + ['2010-06-15T00:00:00.0000+01:01', true], // year has four or more digits + ['20100-06-15T00:00:00.0000+01:01', true], // year has four or more digits + ['-06-15T00:00:00.0000+01:01', false], // year has too few digits + ['2-06-15T00:00:00.0000+01:01', false], // year has too few digits + ['20-06-15T00:00:00.0000+01:01', false], // year has too few digits + ['201-06-15T00:00:00.0000+01:01', false], // year has too few digits + + // Validate month + ['2010-01-15T00:00:00.0000+01:01', true], // month has two digits + ['2010--15T00:00:00.0000+01:01', false], // month has too few digits + ['2010-0-15T00:00:00.0000+01:01', false], // month has too few digits + ['2010-1-15T00:00:00.0000+01:01', false], // month has too few digits + ['2010-111-15T00:00:00.0000+01:01', false], // month has too many digits + ['2010-22-15T00:00:00.0000+01:01', false], // month is too large + + // Validate day + ['2010-01-01T00:00:00.0000+01:01', true], // day has two digits + ['2010-01-T00:00:00.0000+01:01', false], // day has too few digits + ['2010-01-1T00:00:00.0000+01:01', false], // day has too few digits + ['2010-01-200T00:00:00.0000+01:01', false], // day has too many digits + ['2010-01-41T00:00:00.0000+01:01', false], // day is too large + + // Validate time + ['2010-01-01', false], // time must be specified + ['2010-01-0101:00:00.0000+01:01', false], // missing date time separator + ['2010-01-01V01:00:00.0000+01:01', false], // invalid date time separator + ['2010-01-01T01-00-00.0000+01:01', false], // time must use colon separator + + // Validate hour + ['2010-01-01T01:00:00.0000+01:01', true], // hour has two digits + ['2010-01-01T-01:00:00.0000+01:01', false], // hour must be positive + ['2010-01-01T:00:00.0000+01:01', false], // hour has too few digits + ['2010-01-01T1:00:00.0000+01:01', false], // hour has too few digits + ['2010-01-01T220:00:00.0000+01:01', false], // hour has too many digits + ['2010-01-01T32:00:00.0000+01:01', false], // hour is too large + + // Validate minutes + ['2010-01-01T01:00:00.0000+01:01', true], // minute has two digits + ['2010-01-01T01:-00:00.0000+01:01', false], // minute must be positive + ['2010-01-01T01::00.0000+01:01', false], // minute has too few digits + ['2010-01-01T01:0:00.0000+01:01', false], // minute has too few digits + ['2010-01-01T01:100:00.0000+01:01', false], // minute has too many digits + ['2010-01-01T01:60:00.0000+01:01', false], // minute is too large + + // Validate seconds + ['2010-01-01T01:00:00.0000+01:01', true], // second has two digits + ['2010-01-01T01:00:-00.0000+01:01', false], // second must be positive + ['2010-01-01T01:00:.0000+01:01', false], // second has too few digits + ['2010-01-01T01:00:0.0000+01:01', false], // second has too few digits + ['2010-01-01T01:00:100.0000+01:01', false], // second has too many digits + ['2010-01-01T01:00:60.0000+01:01', false], // second is too large + + // Validate milliseconds + ['2010-01-01T01:00:00+01:01', false], // millisecond must be specified + ['2010-01-01T01:00:00.-0000+01:01', false], // millisecond must be positive + ['2010-01-01T01:00:00:0000+01:01', false], // millisecond must use period separator + ['2010-01-01T01:00:00.+01:01', false], // millisecond has too few digits + + // Validate timezone + ['2010-06-15T00:00:00.0000', false], // timezone must be specified + + // Validate timezone offset + ['2010-06-15T00:00:00.0000+01:01', true], // timezone offset can be positive hours and minutes + ['2010-06-15T00:00:00.0000-01:01', true], // timezone offset can be negative hours and minutes + ['2010-06-15T00:00:00.0000~01:01', false], // timezone has postive/negative indicator + ['2010-06-15T00:00:00.000001:01', false], // timezone has postive/negative indicator + ['2010-06-15T00:00:00.0000+00:01Z', false], // timezone invalid trailing characters + ['2010-06-15T00:00:00.0000+00:01 ', false], // timezone invalid trailing characters + + // Validate timezone hour offset + ['2010-06-15T00:00:00.0000+:01', false], // timezone hour offset has too few digits + ['2010-06-15T00:00:00.0000+0:01', false], // timezone hour offset has too few digits + ['2010-06-15T00:00:00.0000+211:01', false], // timezone hour offset too many digits + ['2010-06-15T00:00:00.0000+31:01', false], // timezone hour offset value too large + + // Validate timezone minute offset + ['2010-06-15T00:00:00.0000+00:-01', false], // timezone minute offset must be positive + ['2010-06-15T00:00:00.0000+00.01', false], // timezone minute offset must use colon separator + ['2010-06-15T00:00:00.0000+0101', false], // timezone minute offset must use colon separator + ['2010-06-15T00:00:00.0000+010', false], // timezone minute offset must use colon separator + ['2010-06-15T00:00:00.0000+00', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+00:', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+00:0', false], // timezone minute offset has too few digits + ['2010-06-15T00:00:00.0000+00:211', false], // timezone minute offset has too many digits + ['2010-06-15T00:00:00.0000+01010', false], // timezone minute offset has too many digits + ['2010-06-15T00:00:00.0000+00:61', false], // timezone minute offset is too large + + // Validate timezone UTC + ['2010-06-15T00:00:00.0000Z', true], // UTC timezone can be indicated with Z + ['2010-06-15T00:00:00.0000K', false], // UTC timezone indicator is invalid + ['2010-06-15T00:00:00.0000 Z', false], // UTC timezone indicator has extra space + ['2010-06-15T00:00:00.0000ZZ', false], // UTC timezone indicator invalid trailing characters + ['2010-06-15T00:00:00.0000Z ', false] // UTC timezone indicator invalid trailing characters + ]; + + they('should validate date: $prop', dates, function(item) { + var date = item[0]; + var valid = item[1]; + + /* global ISO_DATE_REGEXP: false */ + expect(ISO_DATE_REGEXP.test(date)).toBe(valid); + }); + }); + }); + + ['month', 'week', 'time', 'date', 'datetime-local'].forEach(function(inputType) { + if (jqLite('').prop('type') !== inputType) { + return; + } + + describe(inputType, function() { + they('should re-validate and dirty when partially editing the input value ($prop event)', + ['keydown', 'wheel', 'mousedown'], + function(validationEvent) { + var mockValidity = {valid: true, badInput: false}; + var inputElm = helper.compileInput('', mockValidity); + + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + + inputElm.triggerHandler({type: validationEvent}); + mockValidity.valid = false; + mockValidity.badInput = true; + $browser.defer.flush(); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$pristine).toBeFalsy(); + } + ); + + they('should do nothing when $prop event fired but validity does not change', + ['keydown', 'wheel', 'mousedown'], + function(validationEvent) { + var mockValidity = {valid: true, badInput: false}; + var inputElm = helper.compileInput('', mockValidity); + + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + + inputElm.triggerHandler({type: validationEvent}); + $browser.defer.flush(); + expect(inputElm).toBeValid(); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + } + ); + + they('should re-validate dirty when already $invalid and partially editing the input value ($prop event)', + ['keydown', 'wheel', 'mousedown'], + function(validationEvent) { + var mockValidity = {valid: false, valueMissing: true, badInput: false}; + var inputElm = helper.compileInput('', mockValidity); + + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + + inputElm.triggerHandler({type: validationEvent}); + mockValidity.valid = false; + mockValidity.valueMissing = true; + mockValidity.badInput = true; + $browser.defer.flush(); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$pristine).toBeFalsy(); + } + ); + + they('should do nothing when already $invalid and $prop event fired but validity does not change', + ['keydown', 'wheel', 'mousedown'], + function(validationEvent) { + var mockValidity = {valid: false, valueMissing: true, badInput: false}; + var inputElm = helper.compileInput('', mockValidity); + + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + + inputElm.triggerHandler({type: validationEvent}); + $browser.defer.flush(); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + } + ); + }); }); describe('number', function() { + // Helpers for min / max tests + var subtract = function(value) { + return value - 5; + }; + + var add = function(value) { + return value + 5; + }; + it('should reset the model if view is invalid', function() { var inputElm = helper.compileInput(''); $rootScope.$apply('age = 123'); expect(inputElm.val()).toBe('123'); - try { - // to allow non-number values, we have to change type so that - // the browser which have number validation will not interfere with - // this test. IE8 won't allow it hence the catch. - inputElm[0].setAttribute('type', 'text'); - } catch (e) {} + // to allow non-number values, we have to change type so that + // the browser which have number validation will not interfere with + // this test. + inputElm[0].setAttribute('type', 'text'); helper.changeInputValueTo('123X'); expect(inputElm.val()).toBe('123X'); @@ -1848,7 +3028,139 @@ describe('input', function() { expect(function() { $rootScope.value = 'one'; var inputElm = helper.compileInput(''); - }).toThrowMinErr('ngModel', 'numfmt', "Expected `one` to be a number"); + }).toThrowMinErr('ngModel', 'numfmt', 'Expected `one` to be a number'); + }); + + + it('should parse exponential notation', function() { + var inputElm = helper.compileInput(''); + + // #.###e+## + $rootScope.form.alias.$setViewValue('1.23214124123412412e+26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.23214124123412412e+26); + + // #.###e## + $rootScope.form.alias.$setViewValue('1.23214124123412412e26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.23214124123412412e26); + + // #.###e-## + $rootScope.form.alias.$setViewValue('1.23214124123412412e-26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.23214124123412412e-26); + + // ####e+## + $rootScope.form.alias.$setViewValue('123214124123412412e+26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(123214124123412412e26); + + // ####e## + $rootScope.form.alias.$setViewValue('123214124123412412e26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(123214124123412412e26); + + // ####e-## + $rootScope.form.alias.$setViewValue('123214124123412412e-26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(123214124123412412e-26); + + // #.###E+## + $rootScope.form.alias.$setViewValue('1.23214124123412412E+26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.23214124123412412e+26); + + // #.###E## + $rootScope.form.alias.$setViewValue('1.23214124123412412E26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.23214124123412412e26); + + // #.###E-## + $rootScope.form.alias.$setViewValue('1.23214124123412412E-26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.23214124123412412e-26); + + // ####E+## + $rootScope.form.alias.$setViewValue('123214124123412412E+26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(123214124123412412e26); + + // ####E## + $rootScope.form.alias.$setViewValue('123214124123412412E26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(123214124123412412e26); + + // ####E-## + $rootScope.form.alias.$setViewValue('123214124123412412E-26'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(123214124123412412e-26); + }); + + it('should not set $error number if any other parser fails', function() { + var inputElm = helper.compileInput(''); + var ctrl = inputElm.controller('ngModel'); + + var previousParserFail = false; + var laterParserFail = false; + + ctrl.$parsers.unshift(function(value) { + return previousParserFail ? undefined : value; + }); + + ctrl.$parsers.push(function(value) { + return laterParserFail ? undefined : value; + }); + + // to allow non-number values, we have to change type so that + // the browser which have number validation will not interfere with + // this test. + inputElm[0].setAttribute('type', 'text'); + + helper.changeInputValueTo('123X'); + expect(inputElm.val()).toBe('123X'); + + expect($rootScope.age).toBeUndefined(); + expect(inputElm).toBeInvalid(); + expect(ctrl.$error.number).toBe(true); + expect(ctrl.$error.parse).toBeFalsy(); + expect(inputElm).toHaveClass('ng-invalid-number'); + expect(inputElm).not.toHaveClass('ng-invalid-parse'); + + previousParserFail = true; + helper.changeInputValueTo('123'); + expect(inputElm.val()).toBe('123'); + + expect($rootScope.age).toBeUndefined(); + expect(inputElm).toBeInvalid(); + expect(ctrl.$error.number).toBeFalsy(); + expect(ctrl.$error.parse).toBe(true); + expect(inputElm).not.toHaveClass('ng-invalid-number'); + expect(inputElm).toHaveClass('ng-invalid-parse'); + + previousParserFail = false; + laterParserFail = true; + + helper.changeInputValueTo('1234'); + expect(inputElm.val()).toBe('1234'); + + expect($rootScope.age).toBeUndefined(); + expect(inputElm).toBeInvalid(); + expect(ctrl.$error.number).toBeFalsy(); + expect(ctrl.$error.parse).toBe(true); + expect(inputElm).not.toHaveClass('ng-invalid-number'); + expect(inputElm).toHaveClass('ng-invalid-parse'); + + laterParserFail = false; + + helper.changeInputValueTo('12345'); + expect(inputElm.val()).toBe('12345'); + + expect($rootScope.age).toBe(12345); + expect(inputElm).toBeValid(); + expect(ctrl.$error.number).toBeFalsy(); + expect(ctrl.$error.parse).toBeFalsy(); + expect(inputElm).not.toHaveClass('ng-invalid-number'); + expect(inputElm).not.toHaveClass('ng-invalid-parse'); }); @@ -1868,13 +3180,41 @@ describe('input', function() { expect($rootScope.form.alias.$error.min).toBeFalsy(); }); + + it('should validate against the viewValue', function() { + var inputElm = helper.compileInput( + ''); + + var ngModelCtrl = inputElm.controller('ngModel'); + ngModelCtrl.$parsers.push(subtract); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(5); + expect($rootScope.form.alias.$error.min).toBeFalsy(); + + ngModelCtrl.$parsers.pop(); + ngModelCtrl.$parsers.push(add); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$error.min).toBeTruthy(); + expect($rootScope.value).toBe(10); + }); + + it('should validate even if min value changes on-the-fly', function() { - $rootScope.min = 10; + $rootScope.min = undefined; var inputElm = helper.compileInput(''); + expect(inputElm).toBeValid(); helper.changeInputValueTo('15'); expect(inputElm).toBeValid(); + $rootScope.min = 10; + $rootScope.$digest(); + expect(inputElm).toBeValid(); + $rootScope.min = 20; $rootScope.$digest(); expect(inputElm).toBeInvalid(); @@ -1891,6 +3231,18 @@ describe('input', function() { $rootScope.$digest(); expect(inputElm).toBeValid(); }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.value = 5; + $rootScope.minVal = 3; + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); + }); describe('ngMin', function() { @@ -1909,13 +3261,40 @@ describe('input', function() { expect($rootScope.form.alias.$error.min).toBeFalsy(); }); + + it('should validate against the viewValue', function() { + var inputElm = helper.compileInput( + ''); + var ngModelCtrl = inputElm.controller('ngModel'); + ngModelCtrl.$parsers.push(subtract); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(5); + expect($rootScope.form.alias.$error.min).toBeFalsy(); + + ngModelCtrl.$parsers.pop(); + ngModelCtrl.$parsers.push(add); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$error.min).toBeTruthy(); + expect($rootScope.value).toBe(10); + }); + + it('should validate even if the ngMin value changes on-the-fly', function() { - $rootScope.min = 10; + $rootScope.min = undefined; var inputElm = helper.compileInput(''); + expect(inputElm).toBeValid(); helper.changeInputValueTo('15'); expect(inputElm).toBeValid(); + $rootScope.min = 10; + $rootScope.$digest(); + expect(inputElm).toBeValid(); + $rootScope.min = 20; $rootScope.$digest(); expect(inputElm).toBeInvalid(); @@ -1932,6 +3311,17 @@ describe('input', function() { $rootScope.$digest(); expect(inputElm).toBeValid(); }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.value = 5; + $rootScope.minVal = 3; + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); }); @@ -1951,13 +3341,40 @@ describe('input', function() { expect($rootScope.form.alias.$error.max).toBeFalsy(); }); + + it('should validate against the viewValue', function() { + var inputElm = helper.compileInput(''); + var ngModelCtrl = inputElm.controller('ngModel'); + ngModelCtrl.$parsers.push(add); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(15); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + + ngModelCtrl.$parsers.pop(); + ngModelCtrl.$parsers.push(subtract); + + helper.changeInputValueTo('15'); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$error.max).toBeTruthy(); + expect($rootScope.value).toBe(10); + }); + + it('should validate even if max value changes on-the-fly', function() { - $rootScope.max = 10; + $rootScope.max = undefined; var inputElm = helper.compileInput(''); + expect(inputElm).toBeValid(); helper.changeInputValueTo('5'); expect(inputElm).toBeValid(); + $rootScope.max = 10; + $rootScope.$digest(); + expect(inputElm).toBeValid(); + $rootScope.max = 0; $rootScope.$digest(); expect(inputElm).toBeInvalid(); @@ -1974,6 +3391,18 @@ describe('input', function() { $rootScope.$digest(); expect(inputElm).toBeValid(); }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.value = 5; + $rootScope.maxVal = 3; + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); + }); describe('ngMax', function() { @@ -1992,11 +3421,38 @@ describe('input', function() { expect($rootScope.form.alias.$error.max).toBeFalsy(); }); - it('should validate even if the ngMax value changes on-the-fly', function() { - $rootScope.max = 10; - var inputElm = helper.compileInput(''); - helper.changeInputValueTo('5'); + it('should validate against the viewValue', function() { + var inputElm = helper.compileInput(''); + var ngModelCtrl = inputElm.controller('ngModel'); + ngModelCtrl.$parsers.push(add); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(15); + expect($rootScope.form.alias.$error.max).toBeFalsy(); + + ngModelCtrl.$parsers.pop(); + ngModelCtrl.$parsers.push(subtract); + + helper.changeInputValueTo('15'); + expect(inputElm).toBeInvalid(); + expect($rootScope.form.alias.$error.max).toBeTruthy(); + expect($rootScope.value).toBe(10); + }); + + + it('should validate even if the ngMax value changes on-the-fly', function() { + $rootScope.max = undefined; + var inputElm = helper.compileInput(''); + expect(inputElm).toBeValid(); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeValid(); + + $rootScope.max = 10; + $rootScope.$digest(); expect(inputElm).toBeValid(); $rootScope.max = 0; @@ -2015,6 +3471,205 @@ describe('input', function() { $rootScope.$digest(); expect(inputElm).toBeValid(); }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.value = 5; + $rootScope.maxVal = 3; + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); + }); + + + forEach({ + step: 'step="{{step}}"', + ngStep: 'ng-step="step"' + }, function(attrHtml, attrName) { + + describe(attrName, function() { + + it('should validate', function() { + $rootScope.step = 10; + $rootScope.value = 20; + var inputElm = helper.compileInput( + ''); + + expect(inputElm.val()).toBe('20'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(20); + expect($rootScope.form.alias.$error.step).toBeFalsy(); + + helper.changeInputValueTo('18'); + expect(inputElm).toBeInvalid(); + expect(inputElm.val()).toBe('18'); + expect($rootScope.value).toBeUndefined(); + expect($rootScope.form.alias.$error.step).toBeTruthy(); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('10'); + expect($rootScope.value).toBe(10); + expect($rootScope.form.alias.$error.step).toBeFalsy(); + + $rootScope.$apply('value = 12'); + expect(inputElm).toBeInvalid(); + expect(inputElm.val()).toBe('12'); + expect($rootScope.value).toBe(12); + expect($rootScope.form.alias.$error.step).toBeTruthy(); + }); + + it('should validate even if the step value changes on-the-fly', function() { + $rootScope.step = 10; + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(10); + + // Step changes, but value matches + $rootScope.$apply('step = 5'); + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(10); + expect($rootScope.form.alias.$error.step).toBeFalsy(); + + // Step changes, value does not match + $rootScope.$apply('step = 6'); + expect(inputElm).toBeInvalid(); + expect($rootScope.value).toBeUndefined(); + expect(inputElm.val()).toBe('10'); + expect($rootScope.form.alias.$error.step).toBeTruthy(); + + // null = valid + $rootScope.$apply('step = null'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(10); + expect(inputElm.val()).toBe('10'); + expect($rootScope.form.alias.$error.step).toBeFalsy(); + + // Step val as string + $rootScope.$apply('step = "7"'); + expect(inputElm).toBeInvalid(); + expect($rootScope.value).toBeUndefined(); + expect(inputElm.val()).toBe('10'); + expect($rootScope.form.alias.$error.step).toBeTruthy(); + + // unparsable string is ignored + $rootScope.$apply('step = "abc"'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(10); + expect(inputElm.val()).toBe('10'); + expect($rootScope.form.alias.$error.step).toBeFalsy(); + }); + + it('should use the correct "step base" when `[min]` is specified', function() { + $rootScope.min = 5; + $rootScope.step = 10; + $rootScope.value = 10; + var inputElm = helper.compileInput( + ''); + var ngModel = inputElm.controller('ngModel'); + + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBe(10); // an initially invalid value should not be changed + + helper.changeInputValueTo('15'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(15); + + $rootScope.$apply('step = 3'); + expect(inputElm.val()).toBe('15'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + + helper.changeInputValueTo('8'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(8); + + $rootScope.$apply('min = 10; step = 20'); + helper.changeInputValueTo('30'); + expect(inputElm.val()).toBe('30'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(30); + + $rootScope.$apply('min = 5'); + expect(inputElm.val()).toBe('30'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + + $rootScope.$apply('step = 0.00000001'); + expect(inputElm.val()).toBe('30'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(30); + + // 0.3 - 0.2 === 0.09999999999999998 + $rootScope.$apply('min = 0.2; step = (0.3 - 0.2)'); + helper.changeInputValueTo('0.3'); + expect(inputElm.val()).toBe('0.3'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + }); + + it('should correctly validate even in cases where the JS floating point arithmetic fails', + function() { + $rootScope.step = 0.1; + var inputElm = helper.compileInput( + ''); + var ngModel = inputElm.controller('ngModel'); + + expect(inputElm.val()).toBe(''); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBeUndefined(); + + helper.changeInputValueTo('0.3'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(0.3); + + helper.changeInputValueTo('2.9999999999999996'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + + // 0.5 % 0.1 === 0.09999999999999998 + helper.changeInputValueTo('0.5'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(0.5); + + // 3.5 % 0.1 === 0.09999999999999981 + helper.changeInputValueTo('3.5'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(3.5); + + // 1.16 % 0.01 === 0.009999999999999896 + // 1.16 * 100 === 115.99999999999999 + $rootScope.step = 0.01; + helper.changeInputValueTo('1.16'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.16); + } + ); + + it('should validate only once after compilation inside ngRepeat', function() { + $rootScope.step = 10; + $rootScope.value = 20; + var inputElm = helper.compileInput('
' + + '' + + '
'); + + expect(helper.validationCounter.step).toBe(1); + }); + + }); }); @@ -2042,7 +3697,7 @@ describe('input', function() { it('should register required on non boolean elements', function() { var inputElm = helper.compileInput('
'); - $rootScope.$apply("value = ''"); + $rootScope.$apply('value = \'\''); expect(inputElm).toBeInvalid(); expect($rootScope.form.alias.$error.required).toBeTruthy(); @@ -2051,10 +3706,20 @@ describe('input', function() { it('should not invalidate number if ng-required=false and viewValue has not been committed', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("required = false"); + $rootScope.$apply('required = false'); expect(inputElm).toBeValid(); }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.value = 'text'; + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.required).toBe(1); + }); }); describe('ngRequired', function() { @@ -2083,7 +3748,7 @@ describe('input', function() { it('should register required on non boolean elements', function() { var inputElm = helper.compileInput('
'); - $rootScope.$apply("value = ''"); + $rootScope.$apply('value = \'\''); expect(inputElm).toBeInvalid(); expect($rootScope.form.numberInput.$error.required).toBeTruthy(); @@ -2104,6 +3769,17 @@ describe('input', function() { expect($rootScope.value).toBeUndefined(); expect($rootScope.form.numberInput.$error.required).toBeFalsy(); }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.value = 'text'; + $rootScope.isRequired = true; + var inputElm = helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.required).toBe(1); + }); }); describe('when the ngRequired expression initially evaluates to false', function() { @@ -2129,7 +3805,7 @@ describe('input', function() { it('should not register required on non boolean elements', function() { var inputElm = helper.compileInput('
'); - $rootScope.$apply("value = ''"); + $rootScope.$apply('value = \'\''); expect(inputElm).toBeValid(); expect($rootScope.form.numberInput.$error.required).toBeFalsy(); @@ -2245,6 +3921,736 @@ describe('input', function() { }); }); + describe('range', function() { + var scope; + + var rangeTestEl = angular.element(''); + var supportsRange = rangeTestEl[0].type === 'range'; + beforeEach(function() { + scope = $rootScope; + }); + + if (supportsRange) { + // This behavior only applies to browsers that implement the range input, which do not + // allow to set a non-number value and will set the value of the input to 50 even when you + // change it directly on the element. + // Other browsers fall back to text inputs, where setting a model value of 50 does not make + // sense if the input value is a string. These browsers will mark the input as invalid instead. + + it('should render as 50 if null', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('25'); + expect(scope.age).toBe(25); + + scope.$apply('age = null'); + + expect(inputElm.val()).toEqual('50'); + }); + + it('should set model to 50 when no value specified and default min/max', function() { + var inputElm = helper.compileInput(''); + + expect(inputElm.val()).toBe('50'); + + scope.$apply('age = null'); + + expect(scope.age).toBe(50); + }); + + it('should parse non-number values to 50 when default min/max', function() { + var inputElm = helper.compileInput(''); + + scope.$apply('age = 10'); + expect(inputElm.val()).toBe('10'); + + helper.changeInputValueTo(''); + expect(scope.age).toBe(50); + expect(inputElm).toBeValid(); + }); + } else { + + it('should reset the model if view is invalid', function() { + var inputElm = helper.compileInput(''); + + scope.$apply('age = 100'); + expect(inputElm.val()).toBe('100'); + + helper.changeInputValueTo('100X'); + expect(inputElm.val()).toBe('100X'); + expect(scope.age).toBeUndefined(); + expect(inputElm).toBeInvalid(); + }); + } + + it('should parse the input value to a Number', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('75'); + expect(scope.age).toBe(75); + }); + + + it('should only invalidate the model if suffering from bad input when the data is parsed', function() { + scope.age = 60; + + var inputElm = helper.compileInput('', { + valid: false, + badInput: true + }); + + expect(inputElm).toBeValid(); + + helper.changeInputValueTo('this-will-fail-because-of-the-badInput-flag'); + + expect(scope.age).toBeUndefined(); + expect(inputElm).toBeInvalid(); + }); + + + it('should throw if the model value is not a number', function() { + expect(function() { + scope.value = 'one'; + var inputElm = helper.compileInput(''); + }).toThrowMinErr('ngModel', 'numfmt', 'Expected `one` to be a number'); + }); + + + describe('min', function() { + + if (supportsRange) { + + it('should initialize correctly with non-default model and min value', function() { + scope.value = -3; + scope.min = -5; + var inputElm = helper.compileInput(''); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('-3'); + expect(scope.value).toBe(-3); + expect(scope.form.alias.$error.min).toBeFalsy(); + }); + + // Browsers that implement range will never allow you to set the value < min values + it('should adjust invalid input values', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.min).toBeFalsy(); + + helper.changeInputValueTo('100'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(100); + expect(scope.form.alias.$error.min).toBeFalsy(); + }); + + it('should set the model to the min val if it is less than the min val', function() { + scope.value = -10; + // Default min is 0 + var inputElm = helper.compileInput(''); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('0'); + expect(scope.value).toBe(0); + + scope.$apply('value = 5; min = 10'); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('10'); + expect(scope.value).toBe(10); + }); + + it('should adjust the element and model value when the min value changes on-the-fly', function() { + scope.min = 10; + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('15'); + expect(inputElm).toBeValid(); + + scope.min = 20; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(20); + expect(inputElm.val()).toBe('20'); + + scope.min = null; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(20); + expect(inputElm.val()).toBe('20'); + + scope.min = '15'; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(20); + expect(inputElm.val()).toBe('20'); + + scope.min = 'abc'; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(20); + expect(inputElm.val()).toBe('20'); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.minVal = 5; + $rootScope.value = 10; + helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); + + } else { + // input[type=range] will become type=text in browsers that don't support it + + it('should validate if "range" is not implemented', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(scope.form.alias.$error.min).toBeTruthy(); + + helper.changeInputValueTo('100'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(100); + expect(scope.form.alias.$error.min).toBeFalsy(); + }); + + it('should not assume a min val of 0 if the min interpolates to a non-number', function() { + scope.value = -10; + var inputElm = helper.compileInput(''); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('-10'); + expect(scope.value).toBe(-10); + expect(scope.form.alias.$error.min).toBeFalsy(); + + helper.changeInputValueTo('-5'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('-5'); + expect(scope.value).toBe(-5); + expect(scope.form.alias.$error.min).toBeFalsy(); + + scope.$apply('max = "null"'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('-5'); + expect(scope.value).toBe(-5); + expect(scope.form.alias.$error.max).toBeFalsy(); + + scope.$apply('max = "asdf"'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('-5'); + expect(scope.value).toBe(-5); + expect(scope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate even if the min value changes on-the-fly', function() { + scope.min = 10; + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('15'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(15); + + scope.min = 20; + scope.$digest(); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(inputElm.val()).toBe('15'); + + scope.min = null; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(15); + expect(inputElm.val()).toBe('15'); + + scope.min = '16'; + scope.$digest(); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(inputElm.val()).toBe('15'); + + scope.min = 'abc'; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(15); + expect(inputElm.val()).toBe('15'); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.minVal = 5; + $rootScope.value = 10; + helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.min).toBe(1); + }); + } + }); + + describe('max', function() { + + if (supportsRange) { + // Browsers that implement range will never allow you to set the value > max value + it('should initialize correctly with non-default model and max value', function() { + scope.value = 130; + scope.max = 150; + var inputElm = helper.compileInput(''); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('130'); + expect(scope.value).toBe(130); + expect(scope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('20'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.max).toBeFalsy(); + + helper.changeInputValueTo('0'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(0); + expect(scope.form.alias.$error.max).toBeFalsy(); + }); + + it('should set the model to the max val if it is greater than the max val', function() { + scope.value = 110; + // Default max is 100 + var inputElm = helper.compileInput(''); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('100'); + expect(scope.value).toBe(100); + + scope.$apply('value = 90; max = 10'); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('10'); + expect(scope.value).toBe(10); + }); + + it('should adjust the element and model value if the max value changes on-the-fly', function() { + scope.max = 10; + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeValid(); + + scope.max = 0; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(0); + expect(inputElm.val()).toBe('0'); + + scope.max = null; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(0); + expect(inputElm.val()).toBe('0'); + + scope.max = '4'; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(0); + expect(inputElm.val()).toBe('0'); + + scope.max = 'abc'; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(0); + expect(inputElm.val()).toBe('0'); + }); + + it('should only validate once after compilation when inside ngRepeat and the value is valid', function() { + $rootScope.maxVal = 5; + $rootScope.value = 5; + helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); + + } else { + it('should validate if "range" is not implemented', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('20'); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(scope.form.alias.$error.max).toBeTruthy(); + + helper.changeInputValueTo('0'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(0); + expect(scope.form.alias.$error.max).toBeFalsy(); + }); + + it('should not assume a max val of 100 if the max attribute interpolates to a non-number', function() { + scope.value = 120; + var inputElm = helper.compileInput(''); + + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('120'); + expect(scope.value).toBe(120); + expect(scope.form.alias.$error.max).toBeFalsy(); + + helper.changeInputValueTo('140'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('140'); + expect(scope.value).toBe(140); + expect(scope.form.alias.$error.max).toBeFalsy(); + + scope.$apply('max = null'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('140'); + expect(scope.value).toBe(140); + expect(scope.form.alias.$error.max).toBeFalsy(); + + scope.$apply('max = "asdf"'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('140'); + expect(scope.value).toBe(140); + expect(scope.form.alias.$error.max).toBeFalsy(); + }); + + it('should validate even if the max value changes on-the-fly', function() { + scope.max = 10; + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + + scope.max = 0; + scope.$digest(); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(inputElm.val()).toBe('5'); + + scope.max = null; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + expect(inputElm.val()).toBe('5'); + + scope.max = '4'; + scope.$digest(); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(inputElm.val()).toBe('5'); + + scope.max = 'abc'; + scope.$digest(); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + expect(inputElm.val()).toBe('5'); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.maxVal = 5; + $rootScope.value = 10; + helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.max).toBe(1); + }); + } + }); + + if (supportsRange) { + + describe('min and max', function() { + + it('should set the correct initial value when min and max are specified', function() { + scope.max = 80; + scope.min = 40; + var inputElm = helper.compileInput(''); + + expect(inputElm.val()).toBe('60'); + expect(scope.value).toBe(60); + }); + + it('should set element and model value to min if max is less than min', function() { + scope.min = 40; + var inputElm = helper.compileInput(''); + + expect(inputElm.val()).toBe('70'); + expect(scope.value).toBe(70); + + scope.max = 20; + scope.$digest(); + + expect(inputElm.val()).toBe('40'); + expect(scope.value).toBe(40); + }); + }); + } + + + describe('step', function() { + + if (supportsRange) { + // Browsers that implement range will never allow you to set a value that doesn't match the step value + // However, currently only Firefox fully implements the spec when setting the value after the step value changes. + // Other browsers fail in various edge cases, which is why they are not tested here. + + it('should round the input value to the nearest step on user input', function() { + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('5'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + expect(scope.form.alias.$error.step).toBeFalsy(); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + + helper.changeInputValueTo('9'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + + helper.changeInputValueTo('7'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + expect(scope.form.alias.$error.step).toBeFalsy(); + + helper.changeInputValueTo('7.5'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + }); + + it('should round the input value to the nearest step when setting the model', function() { + var inputElm = helper.compileInput(''); + + scope.$apply('value = 10'); + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + + scope.$apply('value = 5'); + expect(inputElm.val()).toBe('5'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + expect(scope.form.alias.$error.step).toBeFalsy(); + + scope.$apply('value = 7.5'); + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + + scope.$apply('value = 7'); + expect(inputElm.val()).toBe('5'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(5); + expect(scope.form.alias.$error.step).toBeFalsy(); + + scope.$apply('value = 9'); + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + }); + + it('should only validate once after compilation when inside ngRepeat', function() { + $rootScope.stepVal = 5; + $rootScope.value = 10; + helper.compileInput('
' + + '' + + '
'); + $rootScope.$digest(); + + expect(helper.validationCounter.step).toBe(1); + }); + + } else { + + it('should validate if "range" is not implemented', function() { + scope.step = 10; + scope.value = 20; + var inputElm = helper.compileInput(''); + + expect(inputElm.val()).toBe('20'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(20); + expect(scope.form.alias.$error.step).toBeFalsy(); + + helper.changeInputValueTo('18'); + expect(inputElm).toBeInvalid(); + expect(inputElm.val()).toBe('18'); + expect(scope.value).toBeUndefined(); + expect(scope.form.alias.$error.step).toBeTruthy(); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect(inputElm.val()).toBe('10'); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + + scope.$apply('value = 12'); + expect(inputElm).toBeInvalid(); + expect(inputElm.val()).toBe('12'); + expect(scope.value).toBe(12); + expect(scope.form.alias.$error.step).toBeTruthy(); + }); + + it('should validate even if the step value changes on-the-fly', function() { + scope.step = 10; + var inputElm = helper.compileInput(''); + + helper.changeInputValueTo('10'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + + // Step changes, but value matches + scope.$apply('step = 5'); + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(scope.form.alias.$error.step).toBeFalsy(); + + // Step changes, value does not match + scope.$apply('step = 6'); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(inputElm.val()).toBe('10'); + expect(scope.form.alias.$error.step).toBeTruthy(); + + // null = valid + scope.$apply('step = null'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(inputElm.val()).toBe('10'); + expect(scope.form.alias.$error.step).toBeFalsy(); + + // Step val as string + scope.$apply('step = "7"'); + expect(inputElm).toBeInvalid(); + expect(scope.value).toBeUndefined(); + expect(inputElm.val()).toBe('10'); + expect(scope.form.alias.$error.step).toBeTruthy(); + + // unparsable string is ignored + scope.$apply('step = "abc"'); + expect(inputElm).toBeValid(); + expect(scope.value).toBe(10); + expect(inputElm.val()).toBe('10'); + expect(scope.form.alias.$error.step).toBeFalsy(); + }); + + it('should use the correct "step base" when `[min]` is specified', function() { + $rootScope.min = 5; + $rootScope.step = 10; + $rootScope.value = 10; + var inputElm = helper.compileInput( + ''); + var ngModel = inputElm.controller('ngModel'); + + expect(inputElm.val()).toBe('10'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBe(10); + + helper.changeInputValueTo('15'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(15); + + $rootScope.$apply('step = 3'); + expect(inputElm.val()).toBe('15'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + + helper.changeInputValueTo('8'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(8); + + $rootScope.$apply('min = 10; step = 20; value = 30'); + expect(inputElm.val()).toBe('30'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(30); + + $rootScope.$apply('min = 5'); + expect(inputElm.val()).toBe('30'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + + $rootScope.$apply('step = 0.00000001'); + expect(inputElm.val()).toBe('30'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(30); + + // 0.3 - 0.2 === 0.09999999999999998 + $rootScope.$apply('min = 0.2; step = 0.09999999999999998; value = 0.3'); + expect(inputElm.val()).toBe('0.3'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + }); + + it('should correctly validate even in cases where the JS floating point arithmetic fails', + function() { + $rootScope.step = 0.1; + var inputElm = helper.compileInput( + ''); + var ngModel = inputElm.controller('ngModel'); + + expect(inputElm.val()).toBe(''); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBeUndefined(); + + helper.changeInputValueTo('0.3'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(0.3); + + helper.changeInputValueTo('2.9999999999999996'); + expect(inputElm).toBeInvalid(); + expect(ngModel.$error.step).toBe(true); + expect($rootScope.value).toBeUndefined(); + + // 0.5 % 0.1 === 0.09999999999999998 + helper.changeInputValueTo('0.5'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(0.5); + + // 3.5 % 0.1 === 0.09999999999999981 + helper.changeInputValueTo('3.5'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(3.5); + + // 1.16 % 0.01 === 0.009999999999999896 + // 1.16 * 100 === 115.99999999999999 + $rootScope.step = 0.01; + helper.changeInputValueTo('1.16'); + expect(inputElm).toBeValid(); + expect($rootScope.value).toBe(1.16); + } + ); + } + }); + }); describe('email', function() { @@ -2268,14 +4674,103 @@ describe('input', function() { describe('EMAIL_REGEXP', function() { /* global EMAIL_REGEXP: false */ it('should validate email', function() { + /* basic functionality */ expect(EMAIL_REGEXP.test('a@b.com')).toBe(true); expect(EMAIL_REGEXP.test('a@b.museum')).toBe(true); expect(EMAIL_REGEXP.test('a@B.c')).toBe(true); + /* domain label separation, hyphen-minus, syntax */ + expect(EMAIL_REGEXP.test('a@b.c.')).toBe(false); expect(EMAIL_REGEXP.test('a@.b.c')).toBe(false); expect(EMAIL_REGEXP.test('a@-b.c')).toBe(false); expect(EMAIL_REGEXP.test('a@b-.c')).toBe(false); + expect(EMAIL_REGEXP.test('a@b-c')).toBe(true); + expect(EMAIL_REGEXP.test('a@-')).toBe(false); + expect(EMAIL_REGEXP.test('a@.')).toBe(false); + expect(EMAIL_REGEXP.test('a@host_name')).toBe(false); + /* leading or sole digit */ expect(EMAIL_REGEXP.test('a@3b.c')).toBe(true); + expect(EMAIL_REGEXP.test('a@3')).toBe(true); + /* TLD eMail address */ expect(EMAIL_REGEXP.test('a@b')).toBe(true); + /* domain valid characters */ + expect(EMAIL_REGEXP.test('a@abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789')).toBe(true); + /* domain invalid characters */ + expect(EMAIL_REGEXP.test('a@')).toBe(false); + expect(EMAIL_REGEXP.test('a@ ')).toBe(false); + expect(EMAIL_REGEXP.test('a@!')).toBe(false); + expect(EMAIL_REGEXP.test('a@"')).toBe(false); + expect(EMAIL_REGEXP.test('a@#')).toBe(false); + expect(EMAIL_REGEXP.test('a@$')).toBe(false); + expect(EMAIL_REGEXP.test('a@%')).toBe(false); + expect(EMAIL_REGEXP.test('a@&')).toBe(false); + expect(EMAIL_REGEXP.test('a@\'')).toBe(false); + expect(EMAIL_REGEXP.test('a@(')).toBe(false); + expect(EMAIL_REGEXP.test('a@)')).toBe(false); + expect(EMAIL_REGEXP.test('a@*')).toBe(false); + expect(EMAIL_REGEXP.test('a@+')).toBe(false); + expect(EMAIL_REGEXP.test('a@,')).toBe(false); + expect(EMAIL_REGEXP.test('a@/')).toBe(false); + expect(EMAIL_REGEXP.test('a@:')).toBe(false); + expect(EMAIL_REGEXP.test('a@;')).toBe(false); + expect(EMAIL_REGEXP.test('a@<')).toBe(false); + expect(EMAIL_REGEXP.test('a@=')).toBe(false); + expect(EMAIL_REGEXP.test('a@>')).toBe(false); + expect(EMAIL_REGEXP.test('a@?')).toBe(false); + expect(EMAIL_REGEXP.test('a@@')).toBe(false); + expect(EMAIL_REGEXP.test('a@[')).toBe(false); + expect(EMAIL_REGEXP.test('a@\\')).toBe(false); + expect(EMAIL_REGEXP.test('a@]')).toBe(false); + expect(EMAIL_REGEXP.test('a@^')).toBe(false); + expect(EMAIL_REGEXP.test('a@_')).toBe(false); + expect(EMAIL_REGEXP.test('a@`')).toBe(false); + expect(EMAIL_REGEXP.test('a@{')).toBe(false); + expect(EMAIL_REGEXP.test('a@|')).toBe(false); + expect(EMAIL_REGEXP.test('a@}')).toBe(false); + expect(EMAIL_REGEXP.test('a@~')).toBe(false); + expect(EMAIL_REGEXP.test('a@İ')).toBe(false); + expect(EMAIL_REGEXP.test('a@ı')).toBe(false); + /* domain length, label and total */ + expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')).toBe(true); + expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')).toBe(false); + /* eslint-disable max-len */ + expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')).toBe(true); + expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.x')).toBe(true); + expect(EMAIL_REGEXP.test('a@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xx')).toBe(false); + expect(EMAIL_REGEXP.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xx')).toBe(true); + expect(EMAIL_REGEXP.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.xxx')).toBe(false); + /* eslint-enable */ + /* local-part valid characters and dot-atom syntax */ + expect(EMAIL_REGEXP.test('\'@x')).toBe(true); + expect(EMAIL_REGEXP.test('-!#$%&*+/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ@x')).toBe(true); + expect(EMAIL_REGEXP.test('^_`abcdefghijklmnopqrstuvwxyz{|}~@x')).toBe(true); + expect(EMAIL_REGEXP.test('.@x')).toBe(false); + expect(EMAIL_REGEXP.test('\'.@x')).toBe(false); + expect(EMAIL_REGEXP.test('.\'@x')).toBe(false); + expect(EMAIL_REGEXP.test('\'.\'@x')).toBe(true); + /* local-part invalid characters */ + expect(EMAIL_REGEXP.test('@x')).toBe(false); + expect(EMAIL_REGEXP.test(' @x')).toBe(false); + expect(EMAIL_REGEXP.test('"@x')).toBe(false); + expect(EMAIL_REGEXP.test('(@x')).toBe(false); + expect(EMAIL_REGEXP.test(')@x')).toBe(false); + expect(EMAIL_REGEXP.test(',@x')).toBe(false); + expect(EMAIL_REGEXP.test(':@x')).toBe(false); + expect(EMAIL_REGEXP.test(';@x')).toBe(false); + expect(EMAIL_REGEXP.test('<@x')).toBe(false); + expect(EMAIL_REGEXP.test('>@x')).toBe(false); + expect(EMAIL_REGEXP.test('@@x')).toBe(false); + expect(EMAIL_REGEXP.test('[@x')).toBe(false); + expect(EMAIL_REGEXP.test('\\@x')).toBe(false); + expect(EMAIL_REGEXP.test(']@x')).toBe(false); + expect(EMAIL_REGEXP.test('İ@x')).toBe(false); + expect(EMAIL_REGEXP.test('ı@x')).toBe(false); + /* local-part size limit */ + expect(EMAIL_REGEXP.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@x')).toBe(true); + expect(EMAIL_REGEXP.test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@x')).toBe(false); + /* content (local-part + ‘@’ + domain) is required */ + expect(EMAIL_REGEXP.test('')).toBe(false); + expect(EMAIL_REGEXP.test('a')).toBe(false); + expect(EMAIL_REGEXP.test('aa')).toBe(false); }); }); }); @@ -2300,10 +4795,115 @@ describe('input', function() { describe('URL_REGEXP', function() { - /* global URL_REGEXP: false */ - it('should validate url', function() { - expect(URL_REGEXP.test('http://server:123/path')).toBe(true); - expect(URL_REGEXP.test('a@B.c')).toBe(false); + // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987) + // Note: We are being more lenient, because browsers are too. + var urls = [ + ['scheme://hostname', true], + ['scheme://username:password@host.name:7678/pa/t.h?q=u&e=r&y#fragment', true], + + // Validating `scheme` + ['://example.com', false], + ['0scheme://example.com', false], + ['.scheme://example.com', false], + ['+scheme://example.com', false], + ['-scheme://example.com', false], + ['_scheme://example.com', false], + ['scheme0://example.com', true], + ['scheme.://example.com', true], + ['scheme+://example.com', true], + ['scheme-://example.com', true], + ['scheme_://example.com', false], + + // Validating `:` and `/` after `scheme` + ['scheme//example.com', false], + ['scheme:example.com', true], + ['scheme:/example.com', true], + ['scheme:///example.com', true], + + // Validating `username` and `password` + ['scheme://@example.com', true], + ['scheme://username@example.com', true], + ['scheme://u0s.e+r-n_a~m!e@example.com', true], + ['scheme://u#s$e%r^n&a*m;e@example.com', true], + ['scheme://:password@example.com', true], + ['scheme://username:password@example.com', true], + ['scheme://username:pass:word@example.com', true], + ['scheme://username:p0a.s+s-w_o~r!d@example.com', true], + ['scheme://username:p#a$s%s^w&o*r;d@example.com', true], + + // Validating `hostname` + ['scheme:', false], // Chrome, FF: true + ['scheme://', false], // Chrome, FF: true + ['scheme:// example.com:', false], // Chrome, FF: true + ['scheme://example com:', false], // Chrome, FF: true + ['scheme://:', false], // Chrome, FF: true + ['scheme://?', false], // Chrome, FF: true + ['scheme://#', false], // Chrome, FF: true + ['scheme://username:password@:', false], // Chrome, FF: true + ['scheme://username:password@/', false], // Chrome, FF: true + ['scheme://username:password@?', false], // Chrome, FF: true + ['scheme://username:password@#', false], // Chrome, FF: true + ['scheme://host.name', true], + ['scheme://123.456.789.10', true], + ['scheme://[1234:0000:0000:5678:9abc:0000:0000:def]', true], + ['scheme://[1234:0000:0000:5678:9abc:0000:0000:def]:7678', true], + ['scheme://[1234:0:0:5678:9abc:0:0:def]', true], + ['scheme://[1234::5678:9abc::def]', true], + ['scheme://~`!@$%^&*-_=+|\\;\'",.()[]{}<>', true], + + // Validating `port` + ['scheme://example.com/no-port', true], + ['scheme://example.com:7678', true], + ['scheme://example.com:76T8', false], // Chrome, FF: true + ['scheme://example.com:port', false], // Chrome, FF: true + + // Validating `path` + ['scheme://example.com/', true], + ['scheme://example.com/path', true], + ['scheme://example.com/path/~`!@$%^&*-_=+|\\;:\'",./()[]{}<>', true], + + // Validating `query` + ['scheme://example.com?query', true], + ['scheme://example.com/?query', true], + ['scheme://example.com/path?query', true], + ['scheme://example.com/path?~`!@$%^&*-_=+|\\;:\'",.?/()[]{}<>', true], + + // Validating `fragment` + ['scheme://example.com#fragment', true], + ['scheme://example.com/#fragment', true], + ['scheme://example.com/path#fragment', true], + ['scheme://example.com/path/#fragment', true], + ['scheme://example.com/path?query#fragment', true], + ['scheme://example.com/path?query#~`!@#$%^&*-_=+|\\;:\'",.?/()[]{}<>', true], + + // Validating miscellaneous + ['scheme://☺.✪.⌘.➡/䨹', true], + ['scheme://مثال.إختبار', true], + ['scheme://例子.测试', true], + ['scheme://उदाहरण.परीक्षा', true], + + // Legacy tests + ['http://server:123/path', true], + ['https://server:123/path', true], + ['file:///home/user', true], + ['mailto:user@example.com?subject=Foo', true], + ['r2-d2.c3-p0://localhost/foo', true], + ['abc:/foo', true], + ['http://example.com/path;path', true], + ['http://example.com/[]$\'()*,~)', true], + ['http:', false], // FF: true + ['a@B.c', false], + ['a_B.c', false], + ['0scheme://example.com', false], + ['http://example.com:9999/``', true] + ]; + + they('should validate url: $prop', urls, function(item) { + var url = item[0]; + var valid = item[1]; + + /* global URL_REGEXP: false */ + expect(URL_REGEXP.test(url)).toBe(valid); }); }); }); @@ -2311,26 +4911,38 @@ describe('input', function() { describe('radio', function() { - it('should update the model', function() { + they('should update the model on $prop event', ['click', 'change'], function(event) { var inputElm = helper.compileInput( '' + '' + ''); - $rootScope.$apply("color = 'white'"); + $rootScope.$apply('color = \'white\''); expect(inputElm[0].checked).toBe(true); expect(inputElm[1].checked).toBe(false); expect(inputElm[2].checked).toBe(false); - $rootScope.$apply("color = 'red'"); + $rootScope.$apply('color = \'red\''); expect(inputElm[0].checked).toBe(false); expect(inputElm[1].checked).toBe(true); expect(inputElm[2].checked).toBe(false); - browserTrigger(inputElm[2], 'click'); + if (event === 'change') inputElm[2].checked = true; + browserTrigger(inputElm[2], event); expect($rootScope.color).toBe('blue'); }); + it('should treat the value as a string when evaluating checked-ness', function() { + var inputElm = helper.compileInput( + ''); + + $rootScope.$apply('model = \'0\''); + expect(inputElm[0].checked).toBe(true); + + $rootScope.$apply('model = 0'); + expect(inputElm[0].checked).toBe(false); + }); + it('should allow {{expr}} as value', function() { $rootScope.some = 11; @@ -2350,11 +4962,58 @@ describe('input', function() { browserTrigger(inputElm[1], 'click'); expect($rootScope.value).toBe('red'); - $rootScope.$apply("other = 'non-red'"); + $rootScope.$apply('other = \'non-red\''); expect(inputElm[0].checked).toBe(false); expect(inputElm[1].checked).toBe(false); }); + + + it('should allow the use of ngTrim', function() { + $rootScope.some = 11; + var inputElm = helper.compileInput( + '' + + '' + + '' + + '' + + ''); + + $rootScope.$apply(function() { + $rootScope.value = 'blue'; + $rootScope.some = 'blue'; + }); + + expect(inputElm[0].checked).toBe(false); + expect(inputElm[1].checked).toBe(false); + expect(inputElm[2].checked).toBe(false); + expect(inputElm[3].checked).toBe(true); + expect(inputElm[4].checked).toBe(false); + + browserTrigger(inputElm[1], 'click'); + expect($rootScope.value).toBe('opt2'); + browserTrigger(inputElm[2], 'click'); + expect($rootScope.value).toBe(' opt3 '); + browserTrigger(inputElm[3], 'click'); + expect($rootScope.value).toBe('blue'); + browserTrigger(inputElm[4], 'click'); + expect($rootScope.value).toBe(' blue '); + + $rootScope.$apply('value = \' opt2 \''); + expect(inputElm[1].checked).toBe(false); + $rootScope.$apply('value = \'opt2\''); + expect(inputElm[1].checked).toBe(true); + $rootScope.$apply('value = \' opt3 \''); + expect(inputElm[2].checked).toBe(true); + $rootScope.$apply('value = \'opt3\''); + expect(inputElm[2].checked).toBe(false); + + $rootScope.$apply('value = \'blue\''); + expect(inputElm[3].checked).toBe(true); + expect(inputElm[4].checked).toBe(false); + $rootScope.$apply('value = \' blue \''); + expect(inputElm[3].checked).toBe(false); + expect(inputElm[4].checked).toBe(true); + }); }); @@ -2371,13 +5030,30 @@ describe('input', function() { }); + they('should update the model on $prop event', ['click', 'change'], function(event) { + var inputElm = helper.compileInput(''); + + expect(inputElm[0].checked).toBe(false); + + $rootScope.$apply('checkbox = true'); + expect(inputElm[0].checked).toBe(true); + + $rootScope.$apply('checkbox = false'); + expect(inputElm[0].checked).toBe(false); + + if (event === 'change') inputElm[0].checked = true; + browserTrigger(inputElm[0], event); + expect($rootScope.checkbox).toBe(true); + }); + + it('should format booleans', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("name = false"); + $rootScope.$apply('name = false'); expect(inputElm[0].checked).toBe(false); - $rootScope.$apply("name = true"); + $rootScope.$apply('name = true'); expect(inputElm[0].checked).toBe(true); }); @@ -2397,13 +5073,13 @@ describe('input', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("name = 'y'"); + $rootScope.$apply('name = \'y\''); expect(inputElm[0].checked).toBe(true); - $rootScope.$apply("name = 'n'"); + $rootScope.$apply('name = \'n\''); expect(inputElm[0].checked).toBe(false); - $rootScope.$apply("name = 'something else'"); + $rootScope.$apply('name = \'something else\''); expect(inputElm[0].checked).toBe(false); browserTrigger(inputElm, 'click'); @@ -2417,14 +5093,14 @@ describe('input', function() { it('should throw if ngTrueValue is present and not a constant expression', function() { expect(function() { var inputElm = helper.compileInput(''); - }).toThrowMinErr('ngModel', 'constexpr', "Expected constant expression for `ngTrueValue`, but saw `yes`."); + }).toThrowMinErr('ngModel', 'constexpr', 'Expected constant expression for `ngTrueValue`, but saw `yes`.'); }); it('should throw if ngFalseValue is present and not a constant expression', function() { expect(function() { var inputElm = helper.compileInput(''); - }).toThrowMinErr('ngModel', 'constexpr', "Expected constant expression for `ngFalseValue`, but saw `no`."); + }).toThrowMinErr('ngModel', 'constexpr', 'Expected constant expression for `ngFalseValue`, but saw `no`.'); }); @@ -2448,24 +5124,27 @@ describe('input', function() { }); - it('should set the ngTrueValue when required directive is present', function() { - var inputElm = helper.compileInput(''); + it('should pass validation for "required" when trueValue is a string', function() { + var inputElm = helper.compileInput(''); expect(inputElm).toBeInvalid(); + expect($rootScope.form.cb.$error.required).toBe(true); browserTrigger(inputElm, 'click'); expect(inputElm[0].checked).toBe(true); expect(inputElm).toBeValid(); + expect($rootScope.form.cb.$error.required).toBeUndefined(); }); }); describe('textarea', function() { - it("should process textarea", function() { + it('should process textarea', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("name = 'Adam'"); + $rootScope.$apply('name = \'Adam\''); expect(inputElm.val()).toEqual('Adam'); helper.changeInputValueTo('Shyam'); @@ -2493,12 +5172,52 @@ describe('input', function() { it('should update the dom "value" property and attribute', function() { var inputElm = helper.compileInput(''); - $rootScope.$apply("value = 'something'"); + $rootScope.$apply('value = \'something\''); expect(inputElm[0].value).toBe('something'); expect(inputElm[0].getAttribute('value')).toBe('something'); }); + it('should clear the "dom" value property and attribute when the value is undefined', function() { + var inputElm = helper.compileInput(''); + + $rootScope.$apply('value = "something"'); + + expect(inputElm[0].value).toBe('something'); + expect(inputElm[0].getAttribute('value')).toBe('something'); + + $rootScope.$apply(function() { + delete $rootScope.value; + }); + + expect(inputElm[0].value).toBe(''); + // Support: IE 9-11, Edge + // In IE it is not possible to remove the `value` attribute from an input element. + if (!msie && !isEdge) { + expect(inputElm[0].getAttribute('value')).toBeNull(); + } else { + // Support: IE 9-11, Edge + // This will fail if the Edge bug gets fixed + expect(inputElm[0].getAttribute('value')).toBe('something'); + } + }); + + they('should update the $prop "value" property and attribute after the bound expression changes', { + input: '', + textarea: '' + }, function(tmpl) { + var element = helper.compileInput(tmpl); + + helper.changeInputValueTo('newValue'); + expect(element[0].value).toBe('newValue'); + expect(element[0].getAttribute('value')).toBeNull(); + + $rootScope.$apply(function() { + $rootScope.value = 'anotherValue'; + }); + expect(element[0].value).toBe('anotherValue'); + expect(element[0].getAttribute('value')).toBe('anotherValue'); + }); it('should evaluate and set constant expressions', function() { var inputElm = helper.compileInput('' + @@ -2516,6 +5235,18 @@ describe('input', function() { }); + it('should use strict comparison between model and value', function() { + $rootScope.selected = false; + var inputElm = helper.compileInput('' + + '' + + ''); + + expect(inputElm[0].checked).toBe(true); + expect(inputElm[1].checked).toBe(false); + expect(inputElm[2].checked).toBe(false); + }); + + it('should watch the expression', function() { var inputElm = helper.compileInput(''); diff --git a/test/ng/directive/ngBindSpec.js b/test/ng/directive/ngBindSpec.js index c03afa9123bb..1d5cba43415e 100644 --- a/test/ng/directive/ngBindSpec.js +++ b/test/ng/directive/ngBindSpec.js @@ -46,6 +46,43 @@ describe('ngBind*', function() { expect(element.text()).toEqual('-0false'); })); + they('should jsonify $prop', [[{a: 1}, '{"a":1}'], [true, 'true'], [false, 'false']], function(prop) { + inject(function($rootScope, $compile) { + $rootScope.value = prop[0]; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual(prop[1]); + }); + }); + + it('should use custom toString when present', inject(function($rootScope, $compile) { + $rootScope.value = { + toString: function() { + return 'foo'; + } + }; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('foo'); + })); + + it('should NOT use toString on array objects', inject(function($rootScope, $compile) { + $rootScope.value = []; + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toEqual('[]'); + })); + + + it('should NOT use toString on Date objects', inject(function($rootScope, $compile) { + $rootScope.value = new Date(2014, 10, 10, 0, 0, 0); + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.text()).toBe(JSON.stringify($rootScope.value)); + expect(element.text()).not.toEqual($rootScope.value.toString()); + })); + + it('should one-time bind if the expression starts with two colons', inject(function($rootScope, $compile) { element = $compile('
')($rootScope); $rootScope.a = 'lucas'; @@ -126,7 +163,7 @@ describe('ngBind*', function() { expect(function() { $compile('
'); }).toThrowMinErr('$parse', 'syntax', - "Syntax Error: Token '{' invalid key at column 2 of the expression [{{myHtml}}] starting at [{myHtml}}]"); + 'Syntax Error: Token \'{\' invalid key at column 2 of the expression [{{myHtml}}] starting at [{myHtml}}]'); })); @@ -139,7 +176,17 @@ describe('ngBind*', function() { element = $compile('
')($rootScope); $rootScope.html = '
hello
'; $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual('
hello
'); + expect(lowercase(element.html())).toEqual('
hello
'); + })); + + it('should update html', inject(function($rootScope, $compile, $sce) { + element = $compile('
')($rootScope); + $rootScope.html = 'hello'; + $rootScope.$digest(); + expect(lowercase(element.html())).toEqual('hello'); + $rootScope.html = 'goodbye'; + $rootScope.$digest(); + expect(lowercase(element.html())).toEqual('goodbye'); })); it('should one-time bind if the expression starts with two colons', inject(function($rootScope, $compile) { @@ -173,10 +220,21 @@ describe('ngBind*', function() { element = $compile('
')($rootScope); $rootScope.html = $sce.trustAsHtml('
hello
'); $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual('
hello
'); + expect(lowercase(element.html())).toEqual('
hello
'); + })); + + it('should update html', inject(function($rootScope, $compile, $sce) { + element = $compile('
')($rootScope); + $rootScope.html = $sce.trustAsHtml('hello'); + $rootScope.$digest(); + expect(lowercase(element.html())).toEqual('hello'); + $rootScope.html = $sce.trustAsHtml('goodbye'); + $rootScope.$digest(); + expect(lowercase(element.html())).toEqual('goodbye'); })); - it('should watch the string value to avoid infinite recursion', inject(function($rootScope, $compile, $sce) { + it('should not cause infinite recursion for trustAsHtml object watches', + inject(function($rootScope, $compile, $sce) { // Ref: https://github.com/angular/angular.js/issues/3932 // If the binding is a function that creates a new value on every call via trustAs, we'll // trigger an infinite digest if we don't take care of it. @@ -185,9 +243,36 @@ describe('ngBind*', function() { return $sce.trustAsHtml('
hello
'); }; $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual('
hello
'); + expect(lowercase(element.html())).toEqual('
hello
'); })); + it('should handle custom $sce objects', function() { + function MySafeHtml(val) { this.val = val; } + + module(function($provide) { + $provide.decorator('$sce', function($delegate) { + $delegate.trustAsHtml = function(html) { return new MySafeHtml(html); }; + $delegate.getTrustedHtml = function(mySafeHtml) { return mySafeHtml.val; }; + $delegate.valueOf = function(v) { return v instanceof MySafeHtml ? v.val : v; }; + return $delegate; + }); + }); + + inject(function($rootScope, $compile, $sce) { + // Ref: https://github.com/angular/angular.js/issues/14526 + // Previous code used toString for change detection, which fails for custom objects + // that don't override toString. + element = $compile('
')($rootScope); + var html = 'hello'; + $rootScope.getHtml = function() { return $sce.trustAsHtml(html); }; + $rootScope.$digest(); + expect(lowercase(element.html())).toEqual('hello'); + html = 'goodbye'; + $rootScope.$digest(); + expect(lowercase(element.html())).toEqual('goodbye'); + }); + }); + describe('when $sanitize is available', function() { beforeEach(function() { module('ngSanitize'); }); @@ -195,7 +280,7 @@ describe('ngBind*', function() { element = $compile('
')($rootScope); $rootScope.html = '
hello
'; $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual('
hello
'); + expect(lowercase(element.html())).toEqual('
hello
'); })); }); }); diff --git a/test/ng/directive/ngChangeSpec.js b/test/ng/directive/ngChangeSpec.js index fc4990e14425..8e67328df09a 100644 --- a/test/ng/directive/ngChangeSpec.js +++ b/test/ng/directive/ngChangeSpec.js @@ -1,19 +1,12 @@ 'use strict'; -/* globals getInputCompileHelper: false */ +/* globals generateInputCompilerHelper: false */ describe('ngChange', function() { - var helper, $rootScope; - - beforeEach(function() { - helper = getInputCompileHelper(this); - }); - - afterEach(function() { - helper.dealoc(); - }); + var helper = {}, $rootScope; + generateInputCompilerHelper(helper); beforeEach(inject(function(_$rootScope_) { $rootScope = _$rootScope_; @@ -22,7 +15,7 @@ describe('ngChange', function() { it('should $eval expression after new value is set in the model', function() { helper.compileInput(''); - $rootScope.change = jasmine.createSpy('change').andCallFake(function() { + $rootScope.change = jasmine.createSpy('change').and.callFake(function() { expect($rootScope.value).toBe('new value'); }); diff --git a/test/ng/directive/ngClassSpec.js b/test/ng/directive/ngClassSpec.js index 2b174dcc2a91..74500505fd84 100644 --- a/test/ng/directive/ngClassSpec.js +++ b/test/ng/directive/ngClassSpec.js @@ -33,6 +33,31 @@ describe('ngClass', function() { })); + it('should add new and remove old classes with same names as Object.prototype properties dynamically', inject(function($rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.dynClass = { watch: true, hasOwnProperty: true, isPrototypeOf: true }; + $rootScope.$digest(); + expect(element.hasClass('existing')).toBe(true); + expect(element.hasClass('watch')).toBe(true); + expect(element.hasClass('hasOwnProperty')).toBe(true); + expect(element.hasClass('isPrototypeOf')).toBe(true); + + $rootScope.dynClass.watch = false; + $rootScope.$digest(); + expect(element.hasClass('existing')).toBe(true); + expect(element.hasClass('watch')).toBe(false); + expect(element.hasClass('hasOwnProperty')).toBe(true); + expect(element.hasClass('isPrototypeOf')).toBe(true); + + delete $rootScope.dynClass; + $rootScope.$digest(); + expect(element.hasClass('existing')).toBe(true); + expect(element.hasClass('watch')).toBe(false); + expect(element.hasClass('hasOwnProperty')).toBe(false); + expect(element.hasClass('isPrototypeOf')).toBe(false); + })); + + it('should support adding multiple classes via an array', inject(function($rootScope, $compile) { element = $compile('
')($rootScope); $rootScope.$digest(); @@ -63,6 +88,23 @@ describe('ngClass', function() { expect(element.hasClass('AnotB')).toBeFalsy(); })); + it('should not break when passed non-string/array/object, truthy values', inject(function($rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.hasClass('42')).toBeTruthy(); + })); + + it('should support adding multiple classes via an array mixed with conditionally via a map', inject(function($rootScope, $compile) { + element = $compile('
')($rootScope); + $rootScope.$digest(); + expect(element.hasClass('existing')).toBeTruthy(); + expect(element.hasClass('A')).toBeTruthy(); + expect(element.hasClass('B')).toBeFalsy(); + $rootScope.condition = true; + $rootScope.$digest(); + expect(element.hasClass('B')).toBeTruthy(); + + })); it('should remove classes when the referenced object is the same but its property is changed', inject(function($rootScope, $compile) { @@ -208,21 +250,34 @@ describe('ngClass', function() { })); - it("should allow ngClassOdd/Even on the same element with overlapping classes", inject(function($rootScope, $compile, $animate) { - var className; - - element = $compile('
    • ')($rootScope); + it('should allow ngClassOdd/Even on the same element with overlapping classes', + inject(function($compile, $rootScope) { + element = $compile( + '
        ' + + '
      • ' + + '
      • ' + + '
          ')($rootScope); $rootScope.$digest(); - var e1 = jqLite(element[0].childNodes[1]); - var e2 = jqLite(element[0].childNodes[5]); - expect(e1.hasClass('same')).toBeTruthy(); - expect(e1.hasClass('odd')).toBeTruthy(); - expect(e2.hasClass('same')).toBeTruthy(); - expect(e2.hasClass('odd')).toBeTruthy(); + + var e1 = element.children().eq(0); + var e2 = element.children().eq(1); + var e3 = element.children().eq(2); + + expect(e1).toHaveClass('same'); + expect(e1).toHaveClass('odd'); + expect(e1).not.toHaveClass('even'); + expect(e2).toHaveClass('same'); + expect(e2).not.toHaveClass('odd'); + expect(e2).toHaveClass('even'); + expect(e3).toHaveClass('same'); + expect(e3).toHaveClass('odd'); + expect(e3).not.toHaveClass('even'); }) ); - it('should allow ngClass with overlapping classes', inject(function($rootScope, $compile, $animate) { + it('should allow ngClass with overlapping classes', inject(function($rootScope, $compile) { element = $compile('
          ')($rootScope); $rootScope.$digest(); @@ -230,9 +285,7 @@ describe('ngClass', function() { expect(element).not.toHaveClass('yes'); expect(element).toHaveClass('no'); - $rootScope.$apply(function() { - $rootScope.test = true; - }); + $rootScope.$apply('test = true'); expect(element).toHaveClass('same'); expect(element).toHaveClass('yes'); @@ -263,43 +316,84 @@ describe('ngClass', function() { expect(e2.hasClass('D')).toBeFalsy(); })); - - it('should reapply ngClass when interpolated class attribute changes', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - - $rootScope.$apply(function() { - $rootScope.cls = "two"; - $rootScope.four = true; - }); - expect(element).toHaveClass('one'); - expect(element).toHaveClass('two'); // interpolated - expect(element).toHaveClass('three'); - expect(element).toHaveClass('four'); - - $rootScope.$apply(function() { - $rootScope.cls = "too"; - }); - expect(element).toHaveClass('one'); - expect(element).toHaveClass('too'); // interpolated - expect(element).toHaveClass('three'); - expect(element).toHaveClass('four'); // should still be there - expect(element.hasClass('two')).toBeFalsy(); - - $rootScope.$apply(function() { - $rootScope.cls = "to"; - }); - expect(element).toHaveClass('one'); - expect(element).toHaveClass('to'); // interpolated - expect(element).toHaveClass('three'); - expect(element).toHaveClass('four'); // should still be there - expect(element.hasClass('two')).toBeFalsy(); - expect(element.hasClass('too')).toBeFalsy(); - })); + it('should reapply ngClass when interpolated class attribute changes', + inject(function($compile, $rootScope) { + element = $compile( + '
          ' + + '
          ' + + '
          ' + + '
          ')($rootScope); + var e1 = element.children().eq(0); + var e2 = element.children().eq(1); + + $rootScope.$apply('two = "two"; five = true'); + + expect(e1).toHaveClass('one'); + expect(e1).toHaveClass('two'); + expect(e1).toHaveClass('three'); + expect(e1).not.toHaveClass('four'); + expect(e1).toHaveClass('five'); + expect(e2).toHaveClass('one'); + expect(e2).toHaveClass('two'); + expect(e2).toHaveClass('three'); + expect(e2).not.toHaveClass('four'); + expect(e2).toHaveClass('five'); + + $rootScope.$apply('two = "another-two"'); + + expect(e1).toHaveClass('one'); + expect(e1).not.toHaveClass('two'); + expect(e1).toHaveClass('another-two'); + expect(e1).toHaveClass('three'); + expect(e1).not.toHaveClass('four'); + expect(e1).toHaveClass('five'); + expect(e2).toHaveClass('one'); + expect(e2).not.toHaveClass('two'); + expect(e2).toHaveClass('another-two'); + expect(e2).toHaveClass('three'); + expect(e2).not.toHaveClass('four'); + expect(e2).toHaveClass('five'); + + $rootScope.$apply('two = "two-more"; four = "four"'); + + expect(e1).toHaveClass('one'); + expect(e1).not.toHaveClass('two'); + expect(e1).not.toHaveClass('another-two'); + expect(e1).toHaveClass('two-more'); + expect(e1).toHaveClass('three'); + expect(e1).not.toHaveClass('four'); + expect(e1).toHaveClass('five'); + expect(e2).toHaveClass('one'); + expect(e2).not.toHaveClass('two'); + expect(e2).not.toHaveClass('another-two'); + expect(e2).toHaveClass('two-more'); + expect(e2).toHaveClass('three'); + expect(e2).toHaveClass('four'); + expect(e2).toHaveClass('five'); + + $rootScope.$apply('five = false'); + + expect(e1).toHaveClass('one'); + expect(e1).not.toHaveClass('two'); + expect(e1).not.toHaveClass('another-two'); + expect(e1).toHaveClass('two-more'); + expect(e1).toHaveClass('three'); + expect(e1).not.toHaveClass('four'); + expect(e1).not.toHaveClass('five'); + expect(e2).toHaveClass('one'); + expect(e2).not.toHaveClass('two'); + expect(e2).not.toHaveClass('another-two'); + expect(e2).toHaveClass('two-more'); + expect(e2).toHaveClass('three'); + expect(e2).toHaveClass('four'); + expect(e2).not.toHaveClass('five'); + }) + ); it('should not mess up class value due to observing an interpolated class attribute', inject(function($rootScope, $compile) { $rootScope.foo = true; - $rootScope.$watch("anything", function() { + $rootScope.$watch('anything', function() { $rootScope.foo = false; }); element = $compile('
          ')($rootScope); @@ -372,6 +466,232 @@ describe('ngClass', function() { expect(e2.hasClass('even')).toBeTruthy(); expect(e2.hasClass('odd')).toBeFalsy(); })); + + + it('should add/remove the correct classes when the expression and `$index` change simultaneously', + inject(function($compile, $rootScope) { + element = $compile( + '
          ' + + '
          ' + + '
          ' + + '
          ')($rootScope); + var odd = element.children().eq(0); + var even = element.children().eq(1); + + $rootScope.$apply('$index = 0; foo = "class1"'); + + expect(odd).toHaveClass('class1'); + expect(odd).not.toHaveClass('class2'); + expect(even).not.toHaveClass('class1'); + expect(even).not.toHaveClass('class2'); + + $rootScope.$apply('$index = 1; foo = "class2"'); + + expect(odd).not.toHaveClass('class1'); + expect(odd).not.toHaveClass('class2'); + expect(even).not.toHaveClass('class1'); + expect(even).toHaveClass('class2'); + + $rootScope.$apply('foo = "class1"'); + + expect(odd).not.toHaveClass('class1'); + expect(odd).not.toHaveClass('class2'); + expect(even).toHaveClass('class1'); + expect(even).not.toHaveClass('class2'); + + $rootScope.$apply('$index = 2'); + + expect(odd).toHaveClass('class1'); + expect(odd).not.toHaveClass('class2'); + expect(even).not.toHaveClass('class1'); + expect(even).not.toHaveClass('class2'); + }) + ); + + it('should support mixed array/object variable with a mutating object', + inject(function($rootScope, $compile) { + element = $compile('
          ')($rootScope); + + $rootScope.classVar = [{orange: true}]; + $rootScope.$digest(); + expect(element).toHaveClass('orange'); + + $rootScope.classVar[0].orange = false; + $rootScope.$digest(); + + expect(element).not.toHaveClass('orange'); + }) + ); + + // https://github.com/angular/angular.js/issues/15905 + it('should support a mixed literal-array/object variable', inject(function($rootScope, $compile) { + element = $compile('
          ')($rootScope); + + $rootScope.classVar = {orange: true}; + $rootScope.$digest(); + expect(element).toHaveClass('orange'); + + $rootScope.classVar.orange = false; + $rootScope.$digest(); + + expect(element).not.toHaveClass('orange'); + }) + ); + + it('should support a one-time mixed literal-array/object variable', inject(function($rootScope, $compile) { + element = $compile('
          ')($rootScope); + + $rootScope.classVar1 = {orange: true}; + $rootScope.$digest(); + expect(element).toHaveClass('orange'); + + $rootScope.classVar1.orange = false; + $rootScope.$digest(); + + expect(element).not.toHaveClass('orange'); + }) + ); + + + it('should do value stabilization as expected when one-time binding', + inject(function($rootScope, $compile) { + element = $compile('
          ')($rootScope); + + $rootScope.$apply('className = "foo"'); + expect(element).toHaveClass('foo'); + + $rootScope.$apply('className = "bar"'); + expect(element).toHaveClass('foo'); + }) + ); + + it('should remove the watcher when static array one-time binding', + inject(function($rootScope, $compile) { + element = $compile('
          ')($rootScope); + + $rootScope.$apply('className = "foo"'); + expect(element).toHaveClass('foo'); + + $rootScope.$apply('className = "bar"'); + expect(element).toHaveClass('foo'); + expect(element).not.toHaveClass('bar'); + }) + ); + + it('should remove the watcher when static map one-time binding', + inject(function($rootScope, $compile) { + element = $compile('
          ')($rootScope); + + $rootScope.$apply('fooPresent = true'); + expect(element).toHaveClass('foo'); + + $rootScope.$apply('fooPresent = false'); + expect(element).toHaveClass('foo'); + }) + ); + + it('should track changes of mutating object inside an array', + inject(function($rootScope, $compile) { + $rootScope.classVar = [{orange: true}]; + element = $compile('
          ')($rootScope); + + $rootScope.$digest(); + expect(element).toHaveClass('orange'); + + $rootScope.$apply('classVar[0].orange = false'); + expect(element).not.toHaveClass('orange'); + }) + ); + + //https://github.com/angular/angular.js/issues/15960#issuecomment-299109412 + it('should always reevaluate filters with non-primitive inputs within literals', function() { + module(function($filterProvider) { + $filterProvider.register('foo', valueFn(function(o) { + return o.a || o.b; + })); + }); + + inject(function($rootScope, $compile) { + $rootScope.testObj = {}; + element = $compile('
          ')($rootScope); + + $rootScope.$apply(); + expect(element).not.toHaveClass('x'); + + $rootScope.$apply('testObj.a = true'); + expect(element).toHaveClass('x'); + }); + }); + + describe('large objects', function() { + var getProp; + var veryLargeObj; + + beforeEach(function() { + getProp = jasmine.createSpy('getProp'); + veryLargeObj = {}; + + Object.defineProperty(veryLargeObj, 'prop', { + get: getProp, + enumerable: true + }); + }); + + it('should not be copied when using an expression', inject(function($compile, $rootScope) { + element = $compile('
          ')($rootScope); + $rootScope.fooClass = {foo: veryLargeObj}; + $rootScope.$digest(); + + expect(element).toHaveClass('foo'); + expect(getProp).not.toHaveBeenCalled(); + })); + + it('should not be copied when using a literal', inject(function($compile, $rootScope) { + element = $compile('
          ')($rootScope); + $rootScope.veryLargeObj = veryLargeObj; + $rootScope.$digest(); + + expect(element).toHaveClass('foo'); + expect(getProp).not.toHaveBeenCalled(); + })); + + it('should not be copied when inside an array', inject(function($compile, $rootScope) { + element = $compile('
          ')($rootScope); + $rootScope.veryLargeObj = veryLargeObj; + $rootScope.$digest(); + + expect(element).toHaveClass('foo'); + expect(getProp).not.toHaveBeenCalled(); + })); + + it('should not be copied when using one-time binding', inject(function($compile, $rootScope) { + element = $compile('
          ')($rootScope); + $rootScope.veryLargeObj = veryLargeObj; + $rootScope.$digest(); + + expect(element).toHaveClass('foo'); + expect(element).not.toHaveClass('bar'); + expect(getProp).not.toHaveBeenCalled(); + + $rootScope.$apply('veryLargeObj.bar = "bar"'); + + expect(element).toHaveClass('foo'); + expect(element).not.toHaveClass('bar'); + expect(getProp).not.toHaveBeenCalled(); + + $rootScope.$apply('bar = "bar"'); + + expect(element).toHaveClass('foo'); + expect(element).toHaveClass('bar'); + expect(getProp).not.toHaveBeenCalled(); + + $rootScope.$apply('veryLargeObj.bar = "qux"'); + + expect(element).toHaveClass('foo'); + expect(element).toHaveClass('bar'); + expect(getProp).not.toHaveBeenCalled(); + })); + }); }); describe('ngClass animations', function() { @@ -381,11 +701,11 @@ describe('ngClass animations', function() { dealoc(element); }); - it("should avoid calling addClass accidentally when removeClass is going on", function() { + it('should avoid calling addClass accidentally when removeClass is going on', function() { module('ngAnimateMock'); inject(function($compile, $rootScope, $animate, $timeout) { element = angular.element('
          '); - var body = jqLite(document.body); + var body = jqLite(window.document.body); body.append(element); $compile(element)($rootScope); @@ -414,14 +734,13 @@ describe('ngClass animations', function() { }); }); - it("should consider the ngClass expression evaluation before performing an animation", function() { + it('should combine the ngClass evaluation with the enter animation', function() { //mocks are not used since the enter delegation method is called before addClass and //it makes it impossible to test to see that addClass is called first module('ngAnimate'); module('ngAnimateMock'); - var digestQueue = []; module(function($animateProvider) { $animateProvider.register('.crazy', function() { return { @@ -431,25 +750,9 @@ describe('ngClass animations', function() { } }; }); - - return function($rootScope) { - var before = $rootScope.$$postDigest; - $rootScope.$$postDigest = function() { - var args = arguments; - digestQueue.push(function() { - before.apply($rootScope, args); - }); - }; - }; }); - inject(function($compile, $rootScope, $browser, $rootElement, $animate, $timeout, $document) { - - // Animations need to digest twice in order to be enabled regardless if there are no template HTTP requests. - $rootScope.$digest(); - digestQueue.shift()(); - - $rootScope.$digest(); - digestQueue.shift()(); + inject(function($compile, $rootScope, $browser, $rootElement, $animate, $document) { + $animate.enabled(true); $rootScope.val = 'crazy'; element = angular.element('
          '); @@ -467,29 +770,17 @@ describe('ngClass animations', function() { expect(element.hasClass('crazy')).toBe(false); expect(enterComplete).toBe(false); - expect(digestQueue.length).toBe(1); $rootScope.$digest(); - - $timeout.flush(); - - expect(element.hasClass('crazy')).toBe(true); - expect(enterComplete).toBe(false); - - digestQueue.shift()(); //enter - expect(digestQueue.length).toBe(0); - - //we don't normally need this, but since the timing between digests - //is spaced-out then it is required so that the original digestion - //is kicked into gear + $animate.flush(); $rootScope.$digest(); - $animate.triggerCallbacks(); - expect(element.data('state')).toBe('crazy-enter'); + expect(element.hasClass('crazy')).toBe(true); expect(enterComplete).toBe(true); + expect(element.data('state')).toBe('crazy-enter'); }); }); - it("should not remove classes if they're going to be added back right after", function() { + it('should not remove classes if they\'re going to be added back right after', function() { module('ngAnimateMock'); inject(function($rootScope, $compile, $animate) { diff --git a/test/ng/directive/ngControllerSpec.js b/test/ng/directive/ngControllerSpec.js index bd6d3eee5dfc..f78baa7fff7e 100644 --- a/test/ng/directive/ngControllerSpec.js +++ b/test/ng/directive/ngControllerSpec.js @@ -37,6 +37,10 @@ describe('ngController', function() { this.mark = 'works'; }); + var Foo = function($scope) { + $scope.mark = 'foo'; + }; + $controllerProvider.register('BoundFoo', ['$scope', Foo.bind(null)]); })); afterEach(function() { @@ -50,6 +54,11 @@ describe('ngController', function() { expect(element.text()).toBe('Hello Misko!'); })); + it('should instantiate bound constructor functions', inject(function($compile, $rootScope) { + element = $compile('
          {{mark}}
          ')($rootScope); + $rootScope.$digest(); + expect(element.text()).toBe('foo'); + })); it('should publish controller into scope', inject(function($compile, $rootScope) { element = $compile('
          {{p.mark}}
          ')($rootScope); @@ -141,4 +150,5 @@ describe('ngController', function() { $httpBackend.flush(); expect(controllerScope.name).toBeUndefined(); })); + }); diff --git a/test/ng/directive/ngEventDirsSpec.js b/test/ng/directive/ngEventDirsSpec.js index 19ad69c96d92..4a533a47174f 100644 --- a/test/ng/directive/ngEventDirsSpec.js +++ b/test/ng/directive/ngEventDirsSpec.js @@ -12,15 +12,20 @@ describe('event directives', function() { describe('ngSubmit', function() { it('should get called on form submit', inject(function($rootScope, $compile) { - element = $compile('
          ' + - '' + + element = $compile( + '' + + '' + '
          ')($rootScope); $rootScope.$digest(); + // Support: Chrome 60+ + // We need to add the form to the DOM in order for `submit` events to be properly fired. + window.document.body.appendChild(element[0]); + // prevent submit within the test harness element.on('submit', function(e) { e.preventDefault(); }); - expect($rootScope.submitted).not.toBeDefined(); + expect($rootScope.submitted).toBeUndefined(); browserTrigger(element.children()[0]); expect($rootScope.submitted).toEqual(true); @@ -33,15 +38,20 @@ describe('event directives', function() { } }; - element = $compile('
          ' + - '' + + element = $compile( + '' + + '' + '
          ')($rootScope); $rootScope.$digest(); + // Support: Chrome 60+ (on Windows) + // We need to add the form to the DOM in order for `submit` events to be properly fired. + window.document.body.appendChild(element[0]); + // prevent submit within the test harness element.on('submit', function(e) { e.preventDefault(); }); - expect($rootScope.formSubmitted).not.toBeDefined(); + expect($rootScope.formSubmitted).toBeUndefined(); browserTrigger(element.children()[0]); expect($rootScope.formSubmitted).toEqual('foo'); @@ -78,7 +88,7 @@ describe('event directives', function() { it('should call the listener synchronously inside of $apply if outside of $apply', inject(function($rootScope, $compile) { element = $compile('')($rootScope); - $rootScope.focus = jasmine.createSpy('focus').andCallFake(function() { + $rootScope.focus = jasmine.createSpy('focus').and.callFake(function() { $rootScope.value = 'newValue'; }); @@ -90,23 +100,13 @@ describe('event directives', function() { }); - describe('security', function() { + describe('DOM event object', function() { it('should allow access to the $event object', inject(function($rootScope, $compile) { var scope = $rootScope.$new(); element = $compile('')(scope); element.triggerHandler('click'); expect(scope.e.target).toBe(element[0]); })); - - it('should block access to DOM nodes (e.g. exposed via $event)', inject(function($rootScope, $compile) { - var scope = $rootScope.$new(); - element = $compile('')(scope); - expect(function() { - element.triggerHandler('click'); - }).toThrowMinErr( - '$parse', 'isecdom', 'Referencing DOM nodes in Angular expressions is disallowed! ' + - 'Expression: e = $event.target'); - })); }); describe('blur', function() { @@ -139,7 +139,7 @@ describe('event directives', function() { it('should call the listener synchronously inside of $apply if outside of $apply', inject(function($rootScope, $compile) { element = $compile('')($rootScope); - $rootScope.blur = jasmine.createSpy('blur').andCallFake(function() { + $rootScope.blur = jasmine.createSpy('blur').and.callFake(function() { $rootScope.value = 'newValue'; }); @@ -148,6 +148,133 @@ describe('event directives', function() { expect($rootScope.blur).toHaveBeenCalledOnce(); expect(element.val()).toBe('newValue'); })); + }); + + + it('should call the listener synchronously if the event is triggered inside of a digest', + inject(function($rootScope, $compile) { + var watchedVal; + + element = $compile('')($rootScope); + $rootScope.$watch('value', function(newValue) { + watchedVal = newValue; + }); + $rootScope.click = jasmine.createSpy('click').and.callFake(function() { + $rootScope.value = 'newValue'; + }); + + $rootScope.$apply(function() { + element.triggerHandler('click'); + }); + + expect($rootScope.click).toHaveBeenCalledOnce(); + expect(watchedVal).toEqual('newValue'); + })); + + + it('should call the listener synchronously if the event is triggered outside of a digest', + inject(function($rootScope, $compile) { + var watchedVal; + + element = $compile('')($rootScope); + $rootScope.$watch('value', function(newValue) { + watchedVal = newValue; + }); + $rootScope.click = jasmine.createSpy('click').and.callFake(function() { + $rootScope.value = 'newValue'; + }); + + element.triggerHandler('click'); + + expect($rootScope.click).toHaveBeenCalledOnce(); + expect(watchedVal).toEqual('newValue'); + })); + + + describe('throwing errors in event handlers', function() { + + it('should not stop execution if the event is triggered outside a digest', function() { + + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $compile, $exceptionHandler, $log) { + + element = $compile('')($rootScope); + expect($log.assertEmpty()); + $rootScope.click = function() { + throw new Error('listener error'); + }; + $rootScope.do = function() { + element.triggerHandler('click'); + $log.log('done'); + }; + + $rootScope.do(); + + expect($exceptionHandler.errors).toEqual([Error('listener error')]); + expect($log.log.logs).toEqual([['done']]); + $log.reset(); + }); + }); + + + it('should not stop execution if the event is triggered inside a digest', function() { + + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $compile, $exceptionHandler, $log) { + + element = $compile('')($rootScope); + expect($log.assertEmpty()); + $rootScope.click = function() { + throw new Error('listener error'); + }; + + $rootScope.do = function() { + element.triggerHandler('click'); + $log.log('done'); + }; + + $rootScope.$apply(function() { + $rootScope.do(); + }); + + expect($exceptionHandler.errors).toEqual([Error('listener error')]); + expect($log.log.logs).toEqual([['done']]); + $log.reset(); + }); + }); + + + it('should not stop execution if the event is triggered in a watch expression function', function() { + + module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + }); + + inject(function($rootScope, $compile, $exceptionHandler, $log) { + + element = $compile('')($rootScope); + $rootScope.click = function() { + throw new Error('listener error'); + }; + + $rootScope.$watch(function() { + element.triggerHandler('click'); + $log.log('done'); + }); + + $rootScope.$digest(); + + expect($exceptionHandler.errors).toEqual([Error('listener error'), Error('listener error')]); + expect($log.log.logs).toEqual([['done'], ['done']]); + $log.reset(); + }); + }); }); }); diff --git a/test/ng/directive/ngHrefSpec.js b/test/ng/directive/ngHrefSpec.js new file mode 100644 index 000000000000..876699636ca9 --- /dev/null +++ b/test/ng/directive/ngHrefSpec.js @@ -0,0 +1,141 @@ +'use strict'; + +describe('ngHref', function() { + var element; + + afterEach(function() { + dealoc(element); + }); + + + it('should interpolate the expression and bind to href', inject(function($compile, $rootScope) { + element = $compile('
          ')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toEqual('some/'); + + $rootScope.$apply(function() { + $rootScope.id = 1; + }); + expect(element.attr('href')).toEqual('some/1'); + })); + + + it('should bind href and merge with other attrs', inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + $rootScope.url = 'http://server'; + $rootScope.rel = 'REL'; + $rootScope.$digest(); + expect(element.attr('href')).toEqual('http://server'); + expect(element.attr('rel')).toEqual('REL'); + })); + + + it('should bind href even if no interpolation', inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toEqual('http://server'); + })); + + it('should not set the href if ng-href is empty', inject(function($rootScope, $compile) { + $rootScope.url = null; + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toEqual(undefined); + })); + + it('should remove the href if ng-href changes to empty', inject(function($rootScope, $compile) { + $rootScope.url = 'http://www.google.com/'; + element = $compile('')($rootScope); + $rootScope.$digest(); + + $rootScope.url = null; + $rootScope.$digest(); + expect(element.attr('href')).toEqual(undefined); + })); + + it('should sanitize interpolated url', inject(function($rootScope, $compile) { + /* eslint no-script-url: "off" */ + $rootScope.imageUrl = 'javascript:alert(1);'; + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toBe('unsafe:javascript:alert(1);'); + })); + + it('should sanitize non-interpolated url', inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toBe('unsafe:javascript:alert(1);'); + })); + + + // Support: IE 9-11 only, Edge 12-17 + if (msie || /\bEdge\/1[2-7]\.[\d.]+\b/.test(window.navigator.userAgent)) { + // IE/Edge fail when setting a href to a URL containing a % that isn't a valid escape sequence + // See https://github.com/angular/angular.js/issues/13388 + it('should throw error if ng-href contains a non-escaped percent symbol', inject(function($rootScope, $compile) { + expect(function() { + element = $compile('')($rootScope); + }).toThrow(); + })); + } + + + it('should bind numbers', inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toEqual('1234'); + })); + + + it('should bind and sanitize the result of a (custom) toString() function', inject(function($rootScope, $compile) { + $rootScope.value = {}; + element = $compile('')($rootScope); + $rootScope.$digest(); + expect(element.attr('href')).toEqual('[object Object]'); + + function SafeClass() {} + + SafeClass.prototype.toString = function() { + return 'custom value'; + }; + + $rootScope.value = new SafeClass(); + $rootScope.$digest(); + expect(element.attr('href')).toEqual('custom value'); + + function UnsafeClass() {} + + UnsafeClass.prototype.toString = function() { + return 'javascript:alert(1);'; + }; + + $rootScope.value = new UnsafeClass(); + $rootScope.$digest(); + expect(element.attr('href')).toEqual('unsafe:javascript:alert(1);'); + })); + + + if (isDefined(window.SVGElement)) { + describe('SVGAElement', function() { + it('should interpolate the expression and bind to xlink:href', inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + var child = element.children('a'); + $rootScope.$digest(); + expect(child.attr('xlink:href')).toEqual('some/'); + + $rootScope.$apply(function() { + $rootScope.id = 1; + }); + expect(child.attr('xlink:href')).toEqual('some/1'); + })); + + + it('should bind xlink:href even if no interpolation', inject(function($rootScope, $compile) { + element = $compile('')($rootScope); + var child = element.children('a'); + $rootScope.$digest(); + expect(child.attr('xlink:href')).toEqual('http://server'); + })); + }); + } +}); diff --git a/test/ng/directive/ngIfSpec.js b/test/ng/directive/ngIfSpec.js old mode 100755 new mode 100644 index 9ee94c95b55b..6f7e7cb355f3 --- a/test/ng/directive/ngIfSpec.js +++ b/test/ng/directive/ngIfSpec.js @@ -1,371 +1,393 @@ 'use strict'; describe('ngIf', function() { - var $scope, $compile, element, $compileProvider; - - beforeEach(module(function(_$compileProvider_) { - $compileProvider = _$compileProvider_; - })); - beforeEach(inject(function($rootScope, _$compile_) { - $scope = $rootScope.$new(); - $compile = _$compile_; - element = $compile('
          ')($scope); - })); - - afterEach(function() { - dealoc(element); - }); - function makeIf() { - forEach(arguments, function(expr) { - element.append($compile('
          Hi
          ')($scope)); - }); - $scope.$apply(); - } + describe('basic', function() { + var $scope, $compile, element, $compileProvider; - it('should immediately remove the element if condition is falsy', function() { - makeIf('false', 'undefined', 'null', 'NaN', '\'\'', '0'); - expect(element.children().length).toBe(0); - }); + beforeEach(module(function(_$compileProvider_) { + $compileProvider = _$compileProvider_; + })); + beforeEach(inject(function($rootScope, _$compile_) { + $scope = $rootScope.$new(); + $compile = _$compile_; + element = $compile('
          ')($scope); + })); - it('should leave the element if condition is true', function() { - makeIf('true'); - expect(element.children().length).toBe(1); - }); - - it('should leave the element if the condition is a non-empty string', function() { - makeIf('\'f\'', '\'0\'', '\'false\'', '\'no\'', '\'n\'', '\'[]\''); - expect(element.children().length).toBe(6); - }); + afterEach(function() { + dealoc(element); + }); - it('should leave the element if the condition is an object', function() { - makeIf('[]', '{}'); - expect(element.children().length).toBe(2); - }); + function makeIf() { + forEach(arguments, function(expr) { + element.append($compile('
          Hi
          ')($scope)); + }); + $scope.$apply(); + } - it('should not add the element twice if the condition goes from true to true', function() { - $scope.hello = 'true1'; - makeIf('hello'); - expect(element.children().length).toBe(1); - $scope.$apply('hello = "true2"'); - expect(element.children().length).toBe(1); - }); + it('should immediately remove the element if condition is falsy', function() { + makeIf('false', 'undefined', 'null', 'NaN', '\'\'', '0'); + expect(element.children().length).toBe(0); + }); - it('should not recreate the element if the condition goes from true to true', function() { - $scope.hello = 'true1'; - makeIf('hello'); - element.children().data('flag', true); - $scope.$apply('hello = "true2"'); - expect(element.children().data('flag')).toBe(true); - }); + it('should leave the element if condition is true', function() { + makeIf('true'); + expect(element.children().length).toBe(1); + }); - it('should create then remove the element if condition changes', function() { - $scope.hello = true; - makeIf('hello'); - expect(element.children().length).toBe(1); - $scope.$apply('hello = false'); - expect(element.children().length).toBe(0); - }); + it('should leave the element if the condition is a non-empty string', function() { + makeIf('\'f\'', '\'0\'', '\'false\'', '\'no\'', '\'n\'', '\'[]\''); + expect(element.children().length).toBe(6); + }); - it('should create a new scope every time the expression evaluates to true', function() { - $scope.$apply('value = true'); - element.append($compile( - '
          ' - )($scope)); - $scope.$apply(); - expect(element.children('div').length).toBe(1); - }); + it('should leave the element if the condition is an object', function() { + makeIf('[]', '{}'); + expect(element.children().length).toBe(2); + }); - it('should destroy the child scope every time the expression evaluates to false', function() { - $scope.value = true; - element.append($compile( - '
          ' - )($scope)); - $scope.$apply(); + it('should not add the element twice if the condition goes from true to true', function() { + $scope.hello = 'true1'; + makeIf('hello'); + expect(element.children().length).toBe(1); + $scope.$apply('hello = "true2"'); + expect(element.children().length).toBe(1); + }); - var childScope = element.children().scope(); - var destroyed = false; + it('should not recreate the element if the condition goes from true to true', function() { + $scope.hello = 'true1'; + makeIf('hello'); + element.children().data('flag', true); + $scope.$apply('hello = "true2"'); + expect(element.children().data('flag')).toBe(true); + }); - childScope.$on('$destroy', function() { - destroyed = true; + it('should create then remove the element if condition changes', function() { + $scope.hello = true; + makeIf('hello'); + expect(element.children().length).toBe(1); + $scope.$apply('hello = false'); + expect(element.children().length).toBe(0); }); - $scope.value = false; - $scope.$apply(); + it('should create a new scope every time the expression evaluates to true', function() { + $scope.$apply('value = true'); + element.append($compile( + '
          ' + )($scope)); + $scope.$apply(); + expect(element.children('div').length).toBe(1); + }); - expect(destroyed).toBe(true); - }); + it('should destroy the child scope every time the expression evaluates to false', function() { + $scope.value = true; + element.append($compile( + '
          ' + )($scope)); + $scope.$apply(); - it('should play nice with other elements beside it', function() { - $scope.values = [1, 2, 3, 4]; - element.append($compile( - '
          ' + - '
          ' + - '
          ' - )($scope)); - $scope.$apply(); - expect(element.children().length).toBe(9); - $scope.$apply('values.splice(0,1)'); - expect(element.children().length).toBe(6); - $scope.$apply('values.push(1)'); - expect(element.children().length).toBe(9); - }); + var childScope = element.children().scope(); + var destroyed = false; - it('should play nice with ngInclude on the same element', inject(function($templateCache) { - $templateCache.put('test.html', [200, '{{value}}', {}]); - - $scope.value = 'first'; - element.append($compile( - '
          ' - )($scope)); - $scope.$apply(); - expect(element.text()).toBe('first'); - - $scope.value = 'later'; - $scope.$apply(); - expect(element.text()).toBe(''); - })); - - it('should work with multiple elements', function() { - $scope.show = true; - $scope.things = [1, 2, 3]; - element.append($compile( - '
          before;
          ' + - '
          start;
          ' + - '
          {{thing}};
          ' + - '
          end;
          ' + - '
          after;
          ' - )($scope)); - $scope.$apply(); - expect(element.text()).toBe('before;start;1;2;3;end;after;'); - - $scope.things.push(4); - $scope.$apply(); - expect(element.text()).toBe('before;start;1;2;3;4;end;after;'); - - $scope.show = false; - $scope.$apply(); - expect(element.text()).toBe('before;after;'); - }); + childScope.$on('$destroy', function() { + destroyed = true; + }); - it('should restore the element to its compiled state', function() { - $scope.value = true; - makeIf('value'); - expect(element.children().length).toBe(1); - jqLite(element.children()[0]).removeClass('my-class'); - expect(element.children()[0].className).not.toContain('my-class'); - $scope.$apply('value = false'); - expect(element.children().length).toBe(0); - $scope.$apply('value = true'); - expect(element.children().length).toBe(1); - expect(element.children()[0].className).toContain('my-class'); - }); + $scope.value = false; + $scope.$apply(); - it('should work when combined with an ASYNC template that loads after the first digest', inject(function($httpBackend, $compile, $rootScope) { - $compileProvider.directive('test', function() { - return { - templateUrl: 'test.html' - }; + expect(destroyed).toBe(true); }); - $httpBackend.whenGET('test.html').respond('hello'); - element.append('
          '); - $compile(element)($rootScope); - $rootScope.show = true; - $rootScope.$apply(); - expect(element.text()).toBe(''); - - $httpBackend.flush(); - expect(element.text()).toBe('hello'); - - $rootScope.show = false; - $rootScope.$apply(); - // Note: there are still comments in element! - expect(element.children().length).toBe(0); - expect(element.text()).toBe(''); - })); -}); -describe('ngIf and transcludes', function() { - it('should allow access to directive controller from children when used in a replace template', function() { - var controller; - module(function($compileProvider) { - var directive = $compileProvider.directive; - directive('template', valueFn({ - template: '
          ', - replace: true, - controller: function() { - this.flag = true; - } - })); - directive('test', valueFn({ - require: '^template', - link: function(scope, el, attr, ctrl) { - controller = ctrl; - } - })); + it('should play nice with other elements beside it', function() { + $scope.values = [1, 2, 3, 4]; + element.append($compile( + '
          ' + + '
          ' + + '
          ' + )($scope)); + $scope.$apply(); + expect(element.children().length).toBe(9); + $scope.$apply('values.splice(0,1)'); + expect(element.children().length).toBe(6); + $scope.$apply('values.push(1)'); + expect(element.children().length).toBe(9); }); - inject(function($compile, $rootScope) { - var element = $compile('
          ')($rootScope); - $rootScope.$apply(); - expect(controller.flag).toBe(true); - dealoc(element); - }); - }); - - it('should use the correct transcluded scope', function() { - module(function($compileProvider) { - $compileProvider.directive('iso', valueFn({ - link: function(scope) { - scope.val = 'value in iso scope'; - }, - restrict: 'E', - transclude: true, - template: '
          val={{val}}-
          ', - scope: {} - })); - }); - inject(function($compile, $rootScope) { - $rootScope.val = 'transcluded content'; - var element = $compile('')($rootScope); - $rootScope.$digest(); - expect(trim(element.text())).toEqual('val=value in iso scope-transcluded content'); - dealoc(element); + it('should play nice with ngInclude on the same element', inject(function($templateCache) { + $templateCache.put('test.html', [200, '{{value}}', {}]); + + $scope.value = 'first'; + element.append($compile( + '
          ' + )($scope)); + $scope.$apply(); + expect(element.text()).toBe('first'); + + $scope.value = 'later'; + $scope.$apply(); + expect(element.text()).toBe(''); + })); + + it('should work with multiple elements', function() { + $scope.show = true; + $scope.things = [1, 2, 3]; + element.append($compile( + '
          before;
          ' + + '
          start;
          ' + + '
          {{thing}};
          ' + + '
          end;
          ' + + '
          after;
          ' + )($scope)); + $scope.$apply(); + expect(element.text()).toBe('before;start;1;2;3;end;after;'); + + $scope.things.push(4); + $scope.$apply(); + expect(element.text()).toBe('before;start;1;2;3;4;end;after;'); + + $scope.show = false; + $scope.$apply(); + expect(element.text()).toBe('before;after;'); }); - }); -}); -describe('ngIf animations', function() { - var body, element, $rootElement; - - function html(content) { - $rootElement.html(content); - element = $rootElement.children().eq(0); - return element; - } - - beforeEach(module('ngAnimateMock')); - - beforeEach(module(function() { - // we need to run animation on attached elements; - return function(_$rootElement_) { - $rootElement = _$rootElement_; - body = jqLite(document.body); - body.append($rootElement); - }; - })); - - afterEach(function() { - dealoc(body); - dealoc(element); - }); - - beforeEach(module(function($animateProvider, $provide) { - return function($animate) { - $animate.enabled(true); - }; - })); - - it('should fire off the enter animation', - inject(function($compile, $rootScope, $animate) { - var item; - var $scope = $rootScope.$new(); - element = $compile(html( - '
          ' + - '
          Hi
          ' + - '
          ' - ))($scope); - - $rootScope.$digest(); + it('should restore the element to its compiled state', function() { + $scope.value = true; + makeIf('value'); + expect(element.children().length).toBe(1); + jqLite(element.children()[0]).removeClass('my-class'); + expect(element.children()[0].className).not.toContain('my-class'); + $scope.$apply('value = false'); + expect(element.children().length).toBe(0); $scope.$apply('value = true'); + expect(element.children().length).toBe(1); + expect(element.children()[0].className).toContain('my-class'); + }); + + it('should work when combined with an ASYNC template that loads after the first digest', inject(function($httpBackend, $compile, $rootScope) { + $compileProvider.directive('test', function() { + return { + templateUrl: 'test.html' + }; + }); + $httpBackend.whenGET('test.html').respond('hello'); + element.append('
          '); + $compile(element)($rootScope); + $rootScope.show = true; + $rootScope.$apply(); + expect(element.text()).toBe(''); - item = $animate.queue.shift(); - expect(item.event).toBe('enter'); - expect(item.element.text()).toBe('Hi'); + $httpBackend.flush(); + expect(element.text()).toBe('hello'); - expect(element.children().length).toBe(1); - }) - ); - - it('should fire off the leave animation', - inject(function($compile, $rootScope, $animate) { - var item; - var $scope = $rootScope.$new(); - element = $compile(html( - '
          ' + - '
          Hi
          ' + - '
          ' - ))($scope); - $scope.$apply('value = true'); + $rootScope.show = false; + $rootScope.$apply(); + // Note: there are still comments in element! + expect(element.children().length).toBe(0); + expect(element.text()).toBe(''); + })); - item = $animate.queue.shift(); - expect(item.event).toBe('enter'); - expect(item.element.text()).toBe('Hi'); + it('should not trigger a digest when the element is removed', inject(function($$rAF, $rootScope, $timeout) { + var spy = spyOn($rootScope, '$digest').and.callThrough(); + $scope.hello = true; + makeIf('hello'); expect(element.children().length).toBe(1); - $scope.$apply('value = false'); + $scope.$apply('hello = false'); + spy.calls.reset(); + expect(element.children().length).toBe(0); + // The animation completion is async even without actual animations + $$rAF.flush(); - item = $animate.queue.shift(); - expect(item.event).toBe('leave'); - expect(item.element.text()).toBe('Hi'); + expect(spy).not.toHaveBeenCalled(); + // A digest may have been triggered asynchronously, so check the queue + $timeout.verifyNoPendingTasks(); + })); + }); - expect(element.children().length).toBe(0); - }) - ); - - it('should destroy the previous leave animation if a new one takes place', function() { - module(function($provide) { - $provide.decorator('$animate', function($delegate, $$q) { - var emptyPromise = $$q.defer().promise; - $delegate.leave = function() { - return emptyPromise; - }; - return $delegate; + describe('and transcludes', function() { + it('should allow access to directive controller from children when used in a replace template', function() { + var controller; + module(function($compileProvider) { + var directive = $compileProvider.directive; + directive('template', valueFn({ + template: '
          ', + replace: true, + controller: function() { + this.flag = true; + } + })); + directive('test', valueFn({ + require: '^template', + link: function(scope, el, attr, ctrl) { + controller = ctrl; + } + })); + }); + inject(function($compile, $rootScope) { + var element = $compile('
          ')($rootScope); + $rootScope.$apply(); + expect(controller.flag).toBe(true); + dealoc(element); }); }); - inject(function($compile, $rootScope, $animate) { - var item; - var $scope = $rootScope.$new(); - element = $compile(html( - '
          ' + - '
          Yo
          ' + - '
          ' - ))($scope); - $scope.$apply('value = true'); - var destroyed, inner = element.children(0); - inner.on('$destroy', function() { - destroyed = true; + it('should use the correct transcluded scope', function() { + module(function($compileProvider) { + $compileProvider.directive('iso', valueFn({ + link: function(scope) { + scope.val = 'value in iso scope'; + }, + restrict: 'E', + transclude: true, + template: '
          val={{val}}-
          ', + scope: {} + })); + }); + inject(function($compile, $rootScope) { + $rootScope.val = 'transcluded content'; + var element = $compile('')($rootScope); + $rootScope.$digest(); + expect(trim(element.text())).toEqual('val=value in iso scope-transcluded content'); + dealoc(element); }); + }); + }); - $scope.$apply('value = false'); + describe('and animations', function() { + var body, element, $rootElement; - $scope.$apply('value = true'); + function html(content) { + $rootElement.html(content); + element = $rootElement.children().eq(0); + return element; + } - $scope.$apply('value = false'); + beforeEach(module('ngAnimateMock')); - expect(destroyed).toBe(true); + beforeEach(module(function() { + // we need to run animation on attached elements; + return function(_$rootElement_) { + $rootElement = _$rootElement_; + body = jqLite(window.document.body); + body.append($rootElement); + }; + })); + + afterEach(function() { + dealoc(body); + dealoc(element); }); - }); - it('should work with svg elements when the svg container is transcluded', function() { - module(function($compileProvider) { - $compileProvider.directive('svgContainer', function() { - return { - template: '', - replace: true, - transclude: true - }; + beforeEach(module(function($animateProvider, $provide) { + return function($animate) { + $animate.enabled(true); + }; + })); + + it('should fire off the enter animation', + inject(function($compile, $rootScope, $animate) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
          ' + + '
          Hi
          ' + + '
          ' + ))($scope); + + $rootScope.$digest(); + $scope.$apply('value = true'); + + item = $animate.queue.shift(); + expect(item.event).toBe('enter'); + expect(item.element.text()).toBe('Hi'); + + expect(element.children().length).toBe(1); + }) + ); + + it('should fire off the leave animation', + inject(function($compile, $rootScope, $animate) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
          ' + + '
          Hi
          ' + + '
          ' + ))($scope); + $scope.$apply('value = true'); + + item = $animate.queue.shift(); + expect(item.event).toBe('enter'); + expect(item.element.text()).toBe('Hi'); + + expect(element.children().length).toBe(1); + $scope.$apply('value = false'); + + item = $animate.queue.shift(); + expect(item.event).toBe('leave'); + expect(item.element.text()).toBe('Hi'); + + expect(element.children().length).toBe(0); + }) + ); + + it('should destroy the previous leave animation if a new one takes place', function() { + module(function($provide) { + $provide.decorator('$animate', function($delegate, $$q) { + var emptyPromise = $$q.defer().promise; + emptyPromise.done = noop; + + $delegate.leave = function() { + return emptyPromise; + }; + return $delegate; + }); + }); + inject(function($compile, $rootScope, $animate) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
          ' + + '
          Yo
          ' + + '
          ' + ))($scope); + + $scope.$apply('value = true'); + + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); + + $scope.$apply('value = false'); + + $scope.$apply('value = true'); + + $scope.$apply('value = false'); + + expect(destroyed).toBe(true); }); }); - inject(function($compile, $rootScope) { - element = $compile('')($rootScope); - $rootScope.flag = true; - $rootScope.$apply(); - var circle = element.find('circle'); - expect(circle[0].toString()).toMatch(/SVG/); + it('should work with svg elements when the svg container is transcluded', function() { + module(function($compileProvider) { + $compileProvider.directive('svgContainer', function() { + return { + template: '', + replace: true, + transclude: true + }; + }); + }); + inject(function($compile, $rootScope) { + element = $compile('')($rootScope); + $rootScope.flag = true; + $rootScope.$apply(); + + var circle = element.find('circle'); + expect(circle[0].toString()).toMatch(/SVG/); + }); }); }); }); diff --git a/test/ng/directive/ngIncludeSpec.js b/test/ng/directive/ngIncludeSpec.js index b1ec3e735e1f..e1261c2040e7 100644 --- a/test/ng/directive/ngIncludeSpec.js +++ b/test/ng/directive/ngIncludeSpec.js @@ -1,773 +1,827 @@ 'use strict'; describe('ngInclude', function() { - var element; - afterEach(function() { - dealoc(element); - }); + describe('basic', function() { + var element; + afterEach(function() { + dealoc(element); + }); - function putIntoCache(url, content) { - return function($templateCache) { - $templateCache.put(url, [200, content, {}]); - }; - } - - - it('should trust and use literal urls', inject(function( - $rootScope, $httpBackend, $compile) { - element = $compile('
          ')($rootScope); - $httpBackend.expect('GET', 'url').respond('template text'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('template text'); - dealoc($rootScope); - })); - - - it('should trust and use trusted urls', inject(function($rootScope, $httpBackend, $compile, $sce) { - element = $compile('
          ')($rootScope); - $httpBackend.expect('GET', 'http://foo.bar/url').respond('template text'); - $rootScope.fooUrl = $sce.trustAsResourceUrl('http://foo.bar/url'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('template text'); - dealoc($rootScope); - })); - - - it('should include an external file', inject(putIntoCache('myUrl', '{{name}}'), - function($rootScope, $compile) { - element = jqLite('
          '); - var body = jqLite(document.body); - body.append(element); - element = $compile(element)($rootScope); - $rootScope.name = 'misko'; - $rootScope.url = 'myUrl'; - $rootScope.$digest(); - expect(body.text()).toEqual('misko'); - body.empty(); - })); - - - it('should support ng-include="src" syntax', inject(putIntoCache('myUrl', '{{name}}'), - function($rootScope, $compile) { - element = jqLite('
          '); - jqLite(document.body).append(element); - element = $compile(element)($rootScope); - $rootScope.name = 'Alibaba'; - $rootScope.url = 'myUrl'; - $rootScope.$digest(); - expect(element.text()).toEqual('Alibaba'); - jqLite(document.body).empty(); - })); - - - it('should NOT use untrusted URL expressions ', inject(putIntoCache('myUrl', '{{name}} text'), - function($rootScope, $compile, $sce) { - element = jqLite(''); - jqLite(document.body).append(element); - element = $compile(element)($rootScope); - $rootScope.name = 'chirayu'; - $rootScope.url = 'http://example.com/myUrl'; - expect(function() { $rootScope.$digest(); }).toThrowMinErr( - '$sce', 'insecurl', - /Blocked loading resource from url not allowed by \$sceDelegate policy. URL: https:\/\/fanyv88.com:443\/http\/example.com\/myUrl.*/); - jqLite(document.body).empty(); - })); - - - it('should NOT use mistyped expressions ', inject(putIntoCache('myUrl', '{{name}} text'), - function($rootScope, $compile, $sce) { - element = jqLite(''); - jqLite(document.body).append(element); - element = $compile(element)($rootScope); - $rootScope.name = 'chirayu'; - $rootScope.url = $sce.trustAsUrl('http://example.com/myUrl'); - expect(function() { $rootScope.$digest(); }).toThrowMinErr( - '$sce', 'insecurl', - /Blocked loading resource from url not allowed by \$sceDelegate policy. URL: https:\/\/fanyv88.com:443\/http\/example.com\/myUrl.*/); - jqLite(document.body).empty(); - })); - - - it('should remove previously included text if a falsy value is bound to src', inject( - putIntoCache('myUrl', '{{name}}'), - function($rootScope, $compile) { - element = jqLite('
          '); - element = $compile(element)($rootScope); - $rootScope.name = 'igor'; - $rootScope.url = 'myUrl'; - $rootScope.$digest(); - expect(element.text()).toEqual('igor'); + function putIntoCache(url, content) { + return function($templateCache) { + $templateCache.put(url, [200, content, {}]); + }; + } - $rootScope.url = undefined; - $rootScope.$digest(); - expect(element.text()).toEqual(''); - })); + it('should trust and use literal urls', inject(function( + $rootScope, $httpBackend, $compile) { + element = $compile('
          ')($rootScope); + $httpBackend.expect('GET', 'url').respond('template text'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('template text'); + dealoc($rootScope); + })); - it('should fire $includeContentRequested event on scope after making the xhr call', inject( - function($rootScope, $compile, $httpBackend) { - var contentRequestedSpy = jasmine.createSpy('content requested').andCallFake(function(event) { - expect(event.targetScope).toBe($rootScope); - }); - $httpBackend.whenGET('url').respond('my partial'); - $rootScope.$on('$includeContentRequested', contentRequestedSpy); + it('should trust and use trusted urls', inject(function($rootScope, $httpBackend, $compile, $sce) { + element = $compile('
          ')($rootScope); + $httpBackend.expect('GET', 'http://foo.bar/url').respond('template text'); + $rootScope.fooUrl = $sce.trustAsResourceUrl('http://foo.bar/url'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('template text'); + dealoc($rootScope); + })); - element = $compile('
          ')($rootScope); - $rootScope.$digest(); - expect(contentRequestedSpy).toHaveBeenCalledOnceWith(jasmine.any(Object), 'url'); + it('should include an external file', inject(putIntoCache('myUrl', '{{name}}'), + function($rootScope, $compile) { + element = jqLite('
          '); + var body = jqLite(window.document.body); + body.append(element); + element = $compile(element)($rootScope); + $rootScope.name = 'misko'; + $rootScope.url = 'myUrl'; + $rootScope.$digest(); + expect(body.text()).toEqual('misko'); + body.empty(); + })); - $httpBackend.flush(); - })); - it('should fire $includeContentLoaded event on child scope after linking the content', inject( - function($rootScope, $compile, $templateCache) { - var contentLoadedSpy = jasmine.createSpy('content loaded').andCallFake(function(event) { - expect(event.targetScope.$parent).toBe($rootScope); - expect(element.text()).toBe('partial content'); - }); + it('should support ng-include="src" syntax', inject(putIntoCache('myUrl', '{{name}}'), + function($rootScope, $compile) { + element = jqLite('
          '); + jqLite(window.document.body).append(element); + element = $compile(element)($rootScope); + $rootScope.name = 'Alibaba'; + $rootScope.url = 'myUrl'; + $rootScope.$digest(); + expect(element.text()).toEqual('Alibaba'); + jqLite(window.document.body).empty(); + })); - $templateCache.put('url', [200, 'partial content', {}]); - $rootScope.$on('$includeContentLoaded', contentLoadedSpy); - element = $compile('
          ')($rootScope); - $rootScope.$digest(); + it('should NOT use untrusted URL expressions ', inject(putIntoCache('myUrl', '{{name}} text'), + function($rootScope, $compile, $sce) { + element = jqLite(''); + jqLite(window.document.body).append(element); + element = $compile(element)($rootScope); + $rootScope.name = 'chirayu'; + $rootScope.url = 'http://example.com/myUrl'; + expect(function() { $rootScope.$digest(); }).toThrowMinErr( + '$sce', 'insecurl', + /Blocked loading resource from url not allowed by \$sceDelegate policy. {2}URL: https:\/\/fanyv88.com:443\/http\/example.com\/myUrl.*/); + jqLite(window.document.body).empty(); + })); - expect(contentLoadedSpy).toHaveBeenCalledOnceWith(jasmine.any(Object), 'url'); - })); + it('should NOT use mistyped expressions ', inject(putIntoCache('myUrl', '{{name}} text'), + function($rootScope, $compile, $sce) { + element = jqLite(''); + jqLite(window.document.body).append(element); + element = $compile(element)($rootScope); + $rootScope.name = 'chirayu'; + $rootScope.url = $sce.trustAsUrl('http://example.com/myUrl'); + expect(function() { $rootScope.$digest(); }).toThrowMinErr( + '$sce', 'insecurl', + /Blocked loading resource from url not allowed by \$sceDelegate policy. {2}URL: https:\/\/fanyv88.com:443\/http\/example.com\/myUrl.*/); + jqLite(window.document.body).empty(); + })); - it('should fire $includeContentError event when content request fails', inject( - function($rootScope, $compile, $httpBackend, $templateCache) { - var contentLoadedSpy = jasmine.createSpy('content loaded'), - contentErrorSpy = jasmine.createSpy('content error'); - $rootScope.$on('$includeContentLoaded', contentLoadedSpy); - $rootScope.$on('$includeContentError', contentErrorSpy); + it('should remove previously included text if a falsy value is bound to src', inject( + putIntoCache('myUrl', '{{name}}'), + function($rootScope, $compile) { + element = jqLite('
          '); + element = $compile(element)($rootScope); + $rootScope.name = 'igor'; + $rootScope.url = 'myUrl'; + $rootScope.$digest(); - $httpBackend.expect('GET', 'tpl.html').respond(400, 'nope'); + expect(element.text()).toEqual('igor'); - element = $compile('
          ')($rootScope); + $rootScope.url = undefined; + $rootScope.$digest(); - $rootScope.$apply(function() { - $rootScope.template = 'tpl.html'; - }); - $httpBackend.flush(); + expect(element.text()).toEqual(''); + })); - expect(contentLoadedSpy).not.toHaveBeenCalled(); - expect(contentErrorSpy).toHaveBeenCalledOnceWith(jasmine.any(Object), 'tpl.html'); - expect(element.children('div').contents().length).toBe(0); - })); + it('should fire $includeContentRequested event on scope after making the xhr call', inject( + function($rootScope, $compile, $httpBackend) { + var contentRequestedSpy = jasmine.createSpy('content requested').and.callFake(function(event) { + expect(event.targetScope).toBe($rootScope); + }); + $httpBackend.whenGET('url').respond('my partial'); + $rootScope.$on('$includeContentRequested', contentRequestedSpy); - it('should evaluate onload expression when a partial is loaded', inject( - putIntoCache('myUrl', 'my partial'), - function($rootScope, $compile) { - element = jqLite('
          '); - element = $compile(element)($rootScope); + element = $compile('
          ')($rootScope); + $rootScope.$digest(); - expect($rootScope.loaded).not.toBeDefined(); + expect(contentRequestedSpy).toHaveBeenCalledOnceWith(jasmine.any(Object), 'url'); - $rootScope.url = 'myUrl'; - $rootScope.$digest(); + $httpBackend.flush(); + })); - expect(element.text()).toEqual('my partial'); - expect($rootScope.loaded).toBe(true); - })); + it('should fire $includeContentLoaded event on child scope after linking the content', inject( + function($rootScope, $compile, $templateCache) { + var contentLoadedSpy = jasmine.createSpy('content loaded').and.callFake(function(event) { + expect(event.targetScope.$parent).toBe($rootScope); + expect(element.text()).toBe('partial content'); + }); + $templateCache.put('url', [200, 'partial content', {}]); + $rootScope.$on('$includeContentLoaded', contentLoadedSpy); - it('should create child scope and destroy old one', inject( - function($rootScope, $compile, $httpBackend) { - $httpBackend.whenGET('url1').respond('partial {{$parent.url}}'); - $httpBackend.whenGET('url2').respond(404); + element = $compile('
          ')($rootScope); + $rootScope.$digest(); - element = $compile('
          ')($rootScope); - expect(element.children().scope()).toBeFalsy(); + expect(contentLoadedSpy).toHaveBeenCalledOnceWith(jasmine.any(Object), 'url'); + })); - $rootScope.url = 'url1'; - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.children().scope().$parent).toBe($rootScope); - expect(element.text()).toBe('partial url1'); - $rootScope.url = 'url2'; - $rootScope.$digest(); - $httpBackend.flush(); + it('should fire $includeContentError event when content request fails', inject( + function($rootScope, $compile, $httpBackend, $templateCache) { + var contentLoadedSpy = jasmine.createSpy('content loaded'), + contentErrorSpy = jasmine.createSpy('content error'); - expect($rootScope.$$childHead).toBeFalsy(); - expect(element.text()).toBe(''); + $rootScope.$on('$includeContentLoaded', contentLoadedSpy); + $rootScope.$on('$includeContentError', contentErrorSpy); - $rootScope.url = 'url1'; - $rootScope.$digest(); - expect(element.children().scope().$parent).toBe($rootScope); + $httpBackend.expect('GET', 'tpl.html').respond(400, 'nope'); - $rootScope.url = null; - $rootScope.$digest(); - expect($rootScope.$$childHead).toBeFalsy(); - })); + element = $compile('
          ')($rootScope); + $rootScope.$apply(function() { + $rootScope.template = 'tpl.html'; + }); + $httpBackend.flush(); - it('should do xhr request and cache it', - inject(function($rootScope, $httpBackend, $compile) { - element = $compile('
          ')($rootScope); - $httpBackend.expect('GET', 'myUrl').respond('my partial'); + expect(contentLoadedSpy).not.toHaveBeenCalled(); + expect(contentErrorSpy).toHaveBeenCalledOnceWith(jasmine.any(Object), 'tpl.html'); + expect(element.children('div').contents().length).toBe(0); + })); - $rootScope.url = 'myUrl'; - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('my partial'); - $rootScope.url = null; - $rootScope.$digest(); - expect(element.text()).toEqual(''); + it('should evaluate onload expression when a partial is loaded', inject( + putIntoCache('myUrl', 'my partial'), + function($rootScope, $compile) { + element = jqLite('
          '); + element = $compile(element)($rootScope); - $rootScope.url = 'myUrl'; - $rootScope.$digest(); - expect(element.text()).toEqual('my partial'); - dealoc($rootScope); - })); + expect($rootScope.loaded).not.toBeDefined(); + $rootScope.url = 'myUrl'; + $rootScope.$digest(); - it('should clear content when error during xhr request', - inject(function($httpBackend, $compile, $rootScope) { - element = $compile('
          content
          ')($rootScope); - $httpBackend.expect('GET', 'myUrl').respond(404, ''); + expect(element.text()).toEqual('my partial'); + expect($rootScope.loaded).toBe(true); + })); - $rootScope.url = 'myUrl'; - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toBe(''); - })); + it('should create child scope and destroy old one', inject( + function($rootScope, $compile, $httpBackend) { + $httpBackend.whenGET('url1').respond('partial {{$parent.url}}'); + $httpBackend.whenGET('url2').respond(404); + element = $compile('
          ')($rootScope); + expect(element.children().scope()).toBeFalsy(); - it('should be async even if served from cache', inject( - putIntoCache('myUrl', 'my partial'), - function($rootScope, $compile) { - element = $compile('
          ')($rootScope); + $rootScope.url = 'url1'; + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.children().scope().$parent).toBe($rootScope); + expect(element.text()).toBe('partial url1'); - $rootScope.url = 'myUrl'; + $rootScope.url = 'url2'; + $rootScope.$digest(); + $httpBackend.flush(); - var called = 0; - // we want to assert only during first watch - $rootScope.$watch(function() { - if (!called) expect(element.text()).toBe(''); - called++; - }); + expect($rootScope.$$childHead).toBeFalsy(); + expect(element.text()).toBe(''); - $rootScope.$digest(); - expect(element.text()).toBe('my partial'); - })); + $rootScope.url = 'url1'; + $rootScope.$digest(); + expect(element.children().scope().$parent).toBe($rootScope); + $rootScope.url = null; + $rootScope.$digest(); + expect($rootScope.$$childHead).toBeFalsy(); + })); - it('should discard pending xhr callbacks if a new template is requested before the current ' + - 'finished loading', inject(function($rootScope, $compile, $httpBackend) { - element = jqLite("
          "); - var log = {}; - $rootScope.templateUrl = 'myUrl1'; - $rootScope.logger = function(msg) { - log[msg] = true; - }; - $compile(element)($rootScope); - expect(log).toEqual({}); + it('should do xhr request and cache it', + inject(function($rootScope, $httpBackend, $compile) { + element = $compile('
          ')($rootScope); + $httpBackend.expect('GET', 'myUrl').respond('my partial'); - $httpBackend.expect('GET', 'myUrl1').respond('
          {{logger("url1")}}
          '); - $rootScope.$digest(); - expect(log).toEqual({}); - $rootScope.templateUrl = 'myUrl2'; - $httpBackend.expect('GET', 'myUrl2').respond('
          {{logger("url2")}}
          '); - $httpBackend.flush(); // now that we have two requests pending, flush! + $rootScope.url = 'myUrl'; + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('my partial'); - expect(log).toEqual({ url2: true }); - })); + $rootScope.url = null; + $rootScope.$digest(); + expect(element.text()).toEqual(''); + $rootScope.url = 'myUrl'; + $rootScope.$digest(); + expect(element.text()).toEqual('my partial'); + dealoc($rootScope); + })); - it('should compile only the content', inject(function($compile, $rootScope, $templateCache) { - // regression - var onload = jasmine.createSpy('$includeContentLoaded'); - $rootScope.$on('$includeContentLoaded', onload); - $templateCache.put('tpl.html', [200, 'partial {{tpl}}', {}]); + it('should clear content when error during xhr request', + inject(function($httpBackend, $compile, $rootScope) { + element = $compile('
          content
          ')($rootScope); + $httpBackend.expect('GET', 'myUrl').respond(404, ''); - element = $compile('
          ' + - '
          ')($rootScope); - expect(onload).not.toHaveBeenCalled(); + $rootScope.url = 'myUrl'; + $rootScope.$digest(); + $httpBackend.flush(); - $rootScope.$apply(function() { - $rootScope.tpl = 'tpl.html'; - }); - expect(onload).toHaveBeenCalledOnce(); - - $rootScope.tpl = ''; - $rootScope.$digest(); - dealoc(element); - })); - - - it('should not break attribute bindings on the same element', inject(function($compile, $rootScope, $httpBackend) { - // regression #3793 - - element = $compile('
          ')($rootScope); - $httpBackend.expect('GET', 'url1').respond('template text 1'); - $rootScope.hrefUrl = 'fooUrl1'; - $rootScope.includeUrl = 'url1'; - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toBe('template text 1'); - expect(element.find('span').attr('foo')).toBe('#/fooUrl1'); - - $httpBackend.expect('GET', 'url2').respond('template text 2'); - $rootScope.includeUrl = 'url2'; - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toBe('template text 2'); - expect(element.find('span').attr('foo')).toBe('#/fooUrl1'); - - $rootScope.hrefUrl = 'fooUrl2'; - $rootScope.$digest(); - expect(element.text()).toBe('template text 2'); - expect(element.find('span').attr('foo')).toBe('#/fooUrl2'); - })); - - - it('should exec scripts when jQuery is included', inject(function($compile, $rootScope, $httpBackend) { - if (!jQuery) { - return; - } + expect(element.text()).toBe(''); + })); - element = $compile('
          ')($rootScope); - // the element needs to be appended for the script to run - element.appendTo(document.body); - window._ngIncludeCausesScriptToRun = false; - $httpBackend.expect('GET', 'url1').respond(''); - $rootScope.includeUrl = 'url1'; - $rootScope.$digest(); - $httpBackend.flush(); + it('should be async even if served from cache', inject( + putIntoCache('myUrl', 'my partial'), + function($rootScope, $compile) { + element = $compile('
          ')($rootScope); - expect(window._ngIncludeCausesScriptToRun).toBe(true); + $rootScope.url = 'myUrl'; - // IE8 doesn't like deleting properties of window - window._ngIncludeCausesScriptToRun = undefined; - try { - delete window._ngIncludeCausesScriptToRun; - } catch (e) {} - })); + var called = 0; + // we want to assert only during first watch + $rootScope.$watch(function() { + if (!called) expect(element.text()).toBe(''); + called++; + }); + $rootScope.$digest(); + expect(element.text()).toBe('my partial'); + })); - it('should construct SVG template elements with correct namespace', function() { - if (!window.SVGRectElement) return; - module(function($compileProvider) { - $compileProvider.directive('test', valueFn({ - templateNamespace: 'svg', - templateUrl: 'my-rect.html', - replace: true - })); - }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('my-rect.html').respond(''); - $httpBackend.expectGET('include.svg').respond(''); - element = $compile('')($rootScope); - $httpBackend.flush(); - var child = element.find('rect'); - expect(child.length).toBe(2); - expect(child[0] instanceof SVGRectElement).toBe(true); - }); - }); + it('should discard pending xhr callbacks if a new template is requested before the current ' + + 'finished loading', inject(function($rootScope, $compile, $httpBackend) { + element = jqLite('
          '); + var log = {}; - it('should compile only the template content of an SVG template', function() { - if (!window.SVGRectElement) return; - module(function($compileProvider) { - $compileProvider.directive('test', valueFn({ - templateNamespace: 'svg', - templateUrl: 'my-rect.html', - replace: true - })); - }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('my-rect.html').respond(''); - $httpBackend.expectGET('include.svg').respond(''); - element = $compile('')($rootScope); - $httpBackend.flush(); - expect(element.find('a').length).toBe(0); - }); - }); + $rootScope.templateUrl = 'myUrl1'; + $rootScope.logger = function(msg) { + log[msg] = true; + }; + $compile(element)($rootScope); + expect(log).toEqual({}); + $httpBackend.expect('GET', 'myUrl1').respond('
          {{logger("url1")}}
          '); + $rootScope.$digest(); + expect(log).toEqual({}); + $rootScope.templateUrl = 'myUrl2'; + $httpBackend.expect('GET', 'myUrl2').respond('
          {{logger("url2")}}
          '); + $httpBackend.flush(); // now that we have two requests pending, flush! - describe('autoscroll', function() { - var autoScrollSpy; + expect(log).toEqual({ url2: true }); + })); - function spyOnAnchorScroll() { - return function($provide) { - autoScrollSpy = jasmine.createSpy('$anchorScroll'); - $provide.value('$anchorScroll', autoScrollSpy); - }; - } - function compileAndLink(tpl) { - return function($compile, $rootScope) { - element = $compile(tpl)($rootScope); - }; - } + it('should compile only the content', inject(function($compile, $rootScope, $templateCache) { + // regression - beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock')); - beforeEach(inject( - putIntoCache('template.html', 'CONTENT'), - putIntoCache('another.html', 'CONTENT'))); + var onload = jasmine.createSpy('$includeContentLoaded'); + $rootScope.$on('$includeContentLoaded', onload); + $templateCache.put('tpl.html', [200, 'partial {{tpl}}', {}]); - it('should call $anchorScroll if autoscroll attribute is present', inject( - compileAndLink('
          '), - function($rootScope, $animate, $timeout) { + element = $compile('
          ' + + '
          ')($rootScope); + expect(onload).not.toHaveBeenCalled(); $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; + $rootScope.tpl = 'tpl.html'; }); + expect(onload).toHaveBeenCalledOnce(); + + $rootScope.tpl = ''; + $rootScope.$digest(); + dealoc(element); + })); + + + it('should not break attribute bindings on the same element', inject(function($compile, $rootScope, $httpBackend) { + // regression #3793 + + element = $compile('
          ')($rootScope); + $httpBackend.expect('GET', 'url1').respond('template text 1'); + $rootScope.hrefUrl = 'fooUrl1'; + $rootScope.includeUrl = 'url1'; + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toBe('template text 1'); + expect(element.find('span').attr('foo')).toBe('#/fooUrl1'); - expect(autoScrollSpy).not.toHaveBeenCalled(); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + $httpBackend.expect('GET', 'url2').respond('template text 2'); + $rootScope.includeUrl = 'url2'; + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toBe('template text 2'); + expect(element.find('span').attr('foo')).toBe('#/fooUrl1'); - expect(autoScrollSpy).toHaveBeenCalledOnce(); + $rootScope.hrefUrl = 'fooUrl2'; + $rootScope.$digest(); + expect(element.text()).toBe('template text 2'); + expect(element.find('span').attr('foo')).toBe('#/fooUrl2'); })); - it('should call $anchorScroll if autoscroll evaluates to true', - inject(function($rootScope, $compile, $animate, $timeout) { + it('should exec scripts when jQuery is included', inject(function($compile, $rootScope, $httpBackend) { + if (!jQuery) { + return; + } - element = $compile('
          ')($rootScope); + element = $compile('
          ')($rootScope); - $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; - $rootScope.value = true; + // the element needs to be appended for the script to run + element.appendTo(window.document.body); + window._ngIncludeCausesScriptToRun = false; + $httpBackend.expect('GET', 'url1').respond(''); + $rootScope.includeUrl = 'url1'; + $rootScope.$digest(); + $httpBackend.flush(); + + expect(window._ngIncludeCausesScriptToRun).toBe(true); + + delete window._ngIncludeCausesScriptToRun; + })); + + + it('should construct SVG template elements with correct namespace', function() { + if (!window.SVGRectElement) return; + module(function($compileProvider) { + $compileProvider.directive('test', valueFn({ + templateNamespace: 'svg', + templateUrl: 'my-rect.html', + replace: true + })); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('my-rect.html').respond(''); + $httpBackend.expectGET('include.svg').respond(''); + element = $compile('')($rootScope); + $httpBackend.flush(); + var child = element.find('rect'); + expect(child.length).toBe(2); + // eslint-disable-next-line no-undef + expect(child[0] instanceof SVGRectElement).toBe(true); }); + }); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); - $rootScope.$apply(function() { - $rootScope.tpl = 'another.html'; - $rootScope.value = 'some-string'; + it('should compile only the template content of an SVG template', function() { + if (!window.SVGRectElement) return; + module(function($compileProvider) { + $compileProvider.directive('test', valueFn({ + templateNamespace: 'svg', + templateUrl: 'my-rect.html', + replace: true + })); }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('my-rect.html').respond(''); + $httpBackend.expectGET('include.svg').respond(''); + element = $compile('')($rootScope); + $httpBackend.flush(); + expect(element.find('a').length).toBe(0); + }); + }); - expect($animate.queue.shift().event).toBe('leave'); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); - $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; - $rootScope.value = 100; + it('should not compile template if original scope is destroyed', function() { + module(function($provide) { + $provide.decorator('$compile', function($delegate) { + var result = jasmine.createSpy('$compile').and.callFake($delegate); + result.$$createComment = $delegate.$$createComment; + return result; + }); }); + inject(function($rootScope, $httpBackend, $compile) { + $httpBackend.when('GET', 'url').respond('template text'); + $rootScope.show = true; + element = $compile('
          ')($rootScope); + $rootScope.$digest(); + $rootScope.show = false; + $rootScope.$digest(); + $compile.calls.reset(); + $httpBackend.flush(); + expect($compile).not.toHaveBeenCalled(); + }); + }); - expect($animate.queue.shift().event).toBe('leave'); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); - expect(autoScrollSpy).toHaveBeenCalled(); - expect(autoScrollSpy.callCount).toBe(3); - })); + it('should not trigger a digest when the include is changed', function() { + inject(function($$rAF, $templateCache, $rootScope, $compile, $timeout) { + var spy = spyOn($rootScope, '$digest').and.callThrough(); - it('should not call $anchorScroll if autoscroll attribute is not present', inject( - compileAndLink('
          '), - function($rootScope, $animate, $timeout) { + $templateCache.put('myUrl', 'my template content'); + $templateCache.put('myOtherUrl', 'my other template content'); - $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; + $rootScope.url = 'myUrl'; + element = jqLite('
          '); + element = $compile(element)($rootScope); + $rootScope.$digest(); + // The animation completion is async even without actual animations + $$rAF.flush(); + expect(element.text()).toEqual('my template content'); + + $rootScope.$apply('url = "myOtherUrl"'); + spy.calls.reset(); + expect(element.text()).toEqual('my other template content'); + $$rAF.flush(); + + expect(spy).not.toHaveBeenCalled(); + // A digest may have been triggered asynchronously, so check the queue + $timeout.verifyNoPendingTasks(); }); + }); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); - expect(autoScrollSpy).not.toHaveBeenCalled(); - })); + describe('autoscroll', function() { + var autoScrollSpy; - it('should not call $anchorScroll if autoscroll evaluates to false', - inject(function($rootScope, $compile, $animate, $timeout) { + function spyOnAnchorScroll() { + return function($provide) { + autoScrollSpy = jasmine.createSpy('$anchorScroll'); + $provide.value('$anchorScroll', autoScrollSpy); + }; + } - element = $compile('
          ')($rootScope); + function compileAndLink(tpl) { + return function($compile, $rootScope) { + element = $compile(tpl)($rootScope); + }; + } - $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; - $rootScope.value = false; - }); + beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock')); + beforeEach(inject( + putIntoCache('template.html', 'CONTENT'), + putIntoCache('another.html', 'CONTENT'))); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + it('should call $anchorScroll if autoscroll attribute is present', inject( + compileAndLink('
          '), + function($rootScope, $animate, $timeout) { - $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; - $rootScope.value = undefined; - }); + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + }); - $rootScope.$apply(function() { - $rootScope.tpl = 'template.html'; - $rootScope.value = null; - }); + expect(autoScrollSpy).not.toHaveBeenCalled(); - expect(autoScrollSpy).not.toHaveBeenCalled(); - })); + $animate.flush(); + $rootScope.$digest(); - it('should only call $anchorScroll after the "enter" animation completes', inject( - compileAndLink('
          '), - function($rootScope, $animate, $timeout) { - expect(autoScrollSpy).not.toHaveBeenCalled(); + expect($animate.queue.shift().event).toBe('enter'); + expect(autoScrollSpy).toHaveBeenCalledOnce(); + })); - $rootScope.$apply("tpl = 'template.html'"); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); - expect(autoScrollSpy).toHaveBeenCalledOnce(); - } - )); - }); -}); + it('should call $anchorScroll if autoscroll evaluates to true', + inject(function($rootScope, $compile, $animate, $timeout) { -describe('ngInclude and transcludes', function() { - var element, directive; + element = $compile('
          ')($rootScope); - beforeEach(module(function($compileProvider) { - element = null; - directive = $compileProvider.directive; - })); + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + $rootScope.value = true; + }); - afterEach(function() { - if (element) { - dealoc(element); - } - }); + expect($animate.queue.shift().event).toBe('enter'); + + $rootScope.$apply(function() { + $rootScope.tpl = 'another.html'; + $rootScope.value = 'some-string'; + }); + + expect($animate.queue.shift().event).toBe('leave'); + expect($animate.queue.shift().event).toBe('enter'); + + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + $rootScope.value = 100; + }); + + expect($animate.queue.shift().event).toBe('leave'); + expect($animate.queue.shift().event).toBe('enter'); + + $animate.flush(); + $rootScope.$digest(); + + expect(autoScrollSpy).toHaveBeenCalled(); + expect(autoScrollSpy).toHaveBeenCalledTimes(3); + })); + + + it('should not call $anchorScroll if autoscroll attribute is not present', inject( + compileAndLink('
          '), + function($rootScope, $animate, $timeout) { - it('should allow access to directive controller from children when used in a replace template', function() { - var controller; - module(function() { - directive('template', valueFn({ - template: '
          ', - replace: true, - controller: function() { - this.flag = true; - } + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + }); + + expect($animate.queue.shift().event).toBe('enter'); + expect(autoScrollSpy).not.toHaveBeenCalled(); })); - directive('test', valueFn({ - require: '^template', - link: function(scope, el, attr, ctrl) { - controller = ctrl; - } + + + it('should not call $anchorScroll if autoscroll evaluates to false', + inject(function($rootScope, $compile, $animate, $timeout) { + + element = $compile('
          ')($rootScope); + + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + $rootScope.value = false; + }); + + expect($animate.queue.shift().event).toBe('enter'); + + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + $rootScope.value = undefined; + }); + + $rootScope.$apply(function() { + $rootScope.tpl = 'template.html'; + $rootScope.value = null; + }); + + expect(autoScrollSpy).not.toHaveBeenCalled(); })); - }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('include.html').respond('
          '); - element = $compile('
          ')($rootScope); - $rootScope.$apply(); - $httpBackend.flush(); - expect(controller.flag).toBe(true); - }); - }); - it("should compile its content correctly (although we remove it later)", function() { - var testElement; - module(function() { - directive('test', function() { - return { - link: function(scope, element) { - testElement = element; + it('should only call $anchorScroll after the "enter" animation completes', inject( + compileAndLink('
          '), + function($rootScope, $animate, $timeout) { + expect(autoScrollSpy).not.toHaveBeenCalled(); + + $rootScope.$apply('tpl = \'template.html\''); + expect($animate.queue.shift().event).toBe('enter'); + + $animate.flush(); + $rootScope.$digest(); + + expect(autoScrollSpy).toHaveBeenCalledOnce(); } - }; - }); - }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('include.html').respond(' '); - element = $compile('
          ')($rootScope); - $rootScope.$apply(); - $httpBackend.flush(); - expect(testElement[0].nodeName).toBe('DIV'); + )); }); - }); - it('should link directives on the same element after the content has been loaded', function() { - var contentOnLink; - module(function() { - directive('test', function() { - return { - link: function(scope, element) { - contentOnLink = element.text(); + describe('and transcludes', function() { + var element, directive; + + beforeEach(module(function($compileProvider) { + element = null; + directive = $compileProvider.directive; + })); + + afterEach(function() { + if (element) { + dealoc(element); + } + }); + + it('should allow access to directive controller from children when used in a replace template', function() { + var controller; + module(function() { + directive('template', valueFn({ + template: '
          ', + replace: true, + controller: function() { + this.flag = true; } - }; + })); + directive('test', valueFn({ + require: '^template', + link: function(scope, el, attr, ctrl) { + controller = ctrl; + } + })); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('include.html').respond('
          '); + element = $compile('
          ')($rootScope); + $rootScope.$apply(); + $httpBackend.flush(); + expect(controller.flag).toBe(true); }); }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('include.html').respond('someContent'); - element = $compile('
          ')($rootScope); - $rootScope.$apply(); - $httpBackend.flush(); - expect(contentOnLink).toBe('someContent'); - }); - }); - it('should add the content to the element before compiling it', function() { - var root; - module(function() { - directive('test', function() { - return { - link: function(scope, element) { - root = element.parent().parent(); - } - }; + it('should compile its content correctly (although we remove it later)', function() { + var testElement; + module(function() { + directive('test', function() { + return { + link: function(scope, element) { + testElement = element; + } + }; + }); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('include.html').respond(' '); + element = $compile('
          ')($rootScope); + $rootScope.$apply(); + $httpBackend.flush(); + expect(testElement[0].nodeName).toBe('DIV'); }); + }); - inject(function($compile, $rootScope, $httpBackend) { - $httpBackend.expectGET('include.html').respond(''); - element = $compile('
          ')($rootScope); - $rootScope.$apply(); - $httpBackend.flush(); - expect(root[0]).toBe(element[0]); + + it('should link directives on the same element after the content has been loaded', function() { + var contentOnLink; + module(function() { + directive('test', function() { + return { + link: function(scope, element) { + contentOnLink = element.text(); + } + }; + }); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('include.html').respond('someContent'); + element = $compile('
          ')($rootScope); + $rootScope.$apply(); + $httpBackend.flush(); + expect(contentOnLink).toBe('someContent'); + }); }); - }); -}); -describe('ngInclude animations', function() { - var body, element, $rootElement; - - function html(content) { - $rootElement.html(content); - element = $rootElement.children().eq(0); - return element; - } - - beforeEach(module(function() { - // we need to run animation on attached elements; - return function(_$rootElement_) { - $rootElement = _$rootElement_; - body = jqLite(document.body); - body.append($rootElement); - }; - })); - - afterEach(function() { - dealoc(body); - dealoc(element); + it('should add the content to the element before compiling it', function() { + var root; + module(function() { + directive('test', function() { + return { + link: function(scope, element) { + root = element.parent().parent(); + } + }; + }); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expectGET('include.html').respond(''); + element = $compile('
          ')($rootScope); + $rootScope.$apply(); + $httpBackend.flush(); + expect(root[0]).toBe(element[0]); + }); + }); }); - beforeEach(module('ngAnimateMock')); + describe('and animations', function() { + var body, element, $rootElement; - afterEach(function() { - dealoc(element); - }); + function html(content) { + $rootElement.html(content); + element = $rootElement.children().eq(0); + return element; + } - it('should fire off the enter animation', - inject(function($compile, $rootScope, $templateCache, $animate) { - var item; - - $templateCache.put('enter', [200, '
          data
          ', {}]); - $rootScope.tpl = 'enter'; - element = $compile(html( - '
          ' + - '
          ' - ))($rootScope); - $rootScope.$digest(); + beforeEach(module(function() { + // we need to run animation on attached elements; + return function(_$rootElement_) { + $rootElement = _$rootElement_; + body = jqLite(window.document.body); + body.append($rootElement); + }; + })); - var animation = $animate.queue.pop(); - expect(animation.event).toBe('enter'); - expect(animation.element.text()).toBe('data'); - }) - ); - - it('should fire off the leave animation', - inject(function($compile, $rootScope, $templateCache, $animate) { - var item; - $templateCache.put('enter', [200, '
          data
          ', {}]); - $rootScope.tpl = 'enter'; - element = $compile(html( - '
          ' + - '
          ' - ))($rootScope); - $rootScope.$digest(); + afterEach(function() { + dealoc(body); + dealoc(element); + }); - var animation = $animate.queue.shift(); - expect(animation.event).toBe('enter'); - expect(animation.element.text()).toBe('data'); + beforeEach(module('ngAnimateMock')); - $rootScope.tpl = ''; - $rootScope.$digest(); + afterEach(function() { + dealoc(element); + }); - animation = $animate.queue.shift(); - expect(animation.event).toBe('leave'); - expect(animation.element.text()).toBe('data'); - }) - ); - - it('should animate two separate ngInclude elements', - inject(function($compile, $rootScope, $templateCache, $animate) { - var item; - $templateCache.put('one', [200, 'one', {}]); - $templateCache.put('two', [200, 'two', {}]); - $rootScope.tpl = 'one'; - element = $compile(html( - '
          ' + - '
          ' - ))($rootScope); - $rootScope.$digest(); + it('should fire off the enter animation', + inject(function($compile, $rootScope, $templateCache, $animate) { + var item; + + $templateCache.put('enter', [200, '
          data
          ', {}]); + $rootScope.tpl = 'enter'; + element = $compile(html( + '
          ' + + '
          ' + ))($rootScope); + $rootScope.$digest(); + + var animation = $animate.queue.pop(); + expect(animation.event).toBe('enter'); + expect(animation.element.text()).toBe('data'); + }) + ); + + it('should fire off the leave animation', + inject(function($compile, $rootScope, $templateCache, $animate) { + var item; + $templateCache.put('enter', [200, '
          data
          ', {}]); + $rootScope.tpl = 'enter'; + element = $compile(html( + '
          ' + + '
          ' + ))($rootScope); + $rootScope.$digest(); + + var animation = $animate.queue.shift(); + expect(animation.event).toBe('enter'); + expect(animation.element.text()).toBe('data'); + + $rootScope.tpl = ''; + $rootScope.$digest(); + + animation = $animate.queue.shift(); + expect(animation.event).toBe('leave'); + expect(animation.element.text()).toBe('data'); + }) + ); + + it('should animate two separate ngInclude elements', + inject(function($compile, $rootScope, $templateCache, $animate) { + var item; + $templateCache.put('one', [200, 'one', {}]); + $templateCache.put('two', [200, 'two', {}]); + $rootScope.tpl = 'one'; + element = $compile(html( + '
          ' + + '
          ' + ))($rootScope); + $rootScope.$digest(); + + var item1 = $animate.queue.shift().element; + expect(item1.text()).toBe('one'); + + $rootScope.tpl = 'two'; + $rootScope.$digest(); + + var itemA = $animate.queue.shift().element; + var itemB = $animate.queue.shift().element; + expect(itemA.attr('ng-include')).toBe('tpl'); + expect(itemB.attr('ng-include')).toBe('tpl'); + expect(itemA).not.toEqual(itemB); + }) + ); + + it('should destroy the previous leave animation if a new one takes place', function() { + module(function($provide) { + $provide.decorator('$animate', function($delegate, $$q) { + var emptyPromise = $$q.defer().promise; + emptyPromise.done = noop; + + $delegate.leave = function() { + return emptyPromise; + }; + return $delegate; + }); + }); + inject(function($compile, $rootScope, $animate, $templateCache) { + var item; + var $scope = $rootScope.$new(); + element = $compile(html( + '
          ' + + '
          Yo
          ' + + '
          ' + ))($scope); - var item1 = $animate.queue.shift().element; - expect(item1.text()).toBe('one'); + $templateCache.put('one', [200, '
          one
          ', {}]); + $templateCache.put('two', [200, '
          two
          ', {}]); - $rootScope.tpl = 'two'; - $rootScope.$digest(); + $scope.$apply('inc = "one"'); - var itemA = $animate.queue.shift().element; - var itemB = $animate.queue.shift().element; - expect(itemA.attr('ng-include')).toBe('tpl'); - expect(itemB.attr('ng-include')).toBe('tpl'); - expect(itemA).not.toEqual(itemB); - }) - ); - - it('should destroy the previous leave animation if a new one takes place', function() { - module(function($provide) { - $provide.decorator('$animate', function($delegate, $$q) { - var emptyPromise = $$q.defer().promise; - $delegate.leave = function() { - return emptyPromise; - }; - return $delegate; - }); - }); - inject(function($compile, $rootScope, $animate, $templateCache) { - var item; - var $scope = $rootScope.$new(); - element = $compile(html( - '
          ' + - '
          Yo
          ' + - '
          ' - ))($scope); - - $templateCache.put('one', [200, '
          one
          ', {}]); - $templateCache.put('two', [200, '
          two
          ', {}]); - - $scope.$apply('inc = "one"'); - - var destroyed, inner = element.children(0); - inner.on('$destroy', function() { - destroyed = true; - }); + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); - $scope.$apply('inc = "two"'); + $scope.$apply('inc = "two"'); - $scope.$apply('inc = "one"'); + $scope.$apply('inc = "one"'); - $scope.$apply('inc = "two"'); + $scope.$apply('inc = "two"'); - expect(destroyed).toBe(true); + expect(destroyed).toBe(true); + }); }); }); }); diff --git a/test/ng/directive/ngInitSpec.js b/test/ng/directive/ngInitSpec.js index 9ed930ad39eb..60cab60e69a4 100644 --- a/test/ng/directive/ngInitSpec.js +++ b/test/ng/directive/ngInitSpec.js @@ -9,13 +9,13 @@ describe('ngInit', function() { }); - it("should init model", inject(function($rootScope, $compile) { + it('should init model', inject(function($rootScope, $compile) { element = $compile('
          ')($rootScope); expect($rootScope.a).toEqual(123); })); - it("should be evaluated before ngInclude", inject(function($rootScope, $templateCache, $compile) { + it('should be evaluated before ngInclude', inject(function($rootScope, $templateCache, $compile) { $templateCache.put('template1.tpl', '1'); $templateCache.put('template2.tpl', '2'); $rootScope.template = 'template1.tpl'; @@ -27,7 +27,7 @@ describe('ngInit', function() { })); - it("should be evaluated after ngController", function() { + it('should be evaluated after ngController', function() { module(function($controllerProvider) { $controllerProvider.register('TestCtrl', function($scope) {}); }); diff --git a/test/ng/directive/ngListSpec.js b/test/ng/directive/ngListSpec.js index dd06913ba029..3ea606978345 100644 --- a/test/ng/directive/ngListSpec.js +++ b/test/ng/directive/ngListSpec.js @@ -1,19 +1,12 @@ 'use strict'; -/* globals getInputCompileHelper: false */ +/* globals generateInputCompilerHelper: false */ describe('ngList', function() { - var helper, $rootScope; - - beforeEach(function() { - helper = getInputCompileHelper(this); - }); - - afterEach(function() { - helper.dealoc(); - }); + var helper = {}, $rootScope; + generateInputCompilerHelper(helper); beforeEach(inject(function(_$rootScope_) { $rootScope = _$rootScope_; @@ -23,7 +16,7 @@ describe('ngList', function() { var inputElm = helper.compileInput(''); // model -> view - $rootScope.$apply("list = ['x', 'y', 'z']"); + $rootScope.$apply('list = [\'x\', \'y\', \'z\']'); expect(inputElm.val()).toBe('x, y, z'); // view -> model @@ -32,7 +25,7 @@ describe('ngList', function() { }); - it("should not clobber text if model changes due to itself", function() { + it('should not clobber text if model changes due to itself', function() { // When the user types 'a,b' the 'a,' stage parses to ['a'] but if the // $parseModel function runs it will change to 'a', in essence preventing // the user from ever typing ','. @@ -86,7 +79,7 @@ describe('ngList', function() { }); - it("should join the list back together with the custom separator", function() { + it('should join the list back together with the custom separator', function() { var inputElm = helper.compileInput(''); $rootScope.$apply(function() { @@ -126,17 +119,23 @@ describe('ngList', function() { }); - it("should not trim whitespace from each list item", function() { + it('should not trim whitespace from each list item', function() { helper.compileInput(''); helper.changeInputValueTo('a | b'); expect($rootScope.list).toEqual(['a ',' b']); }); - it("should support splitting on newlines", function() { - helper.compileInput(''); helper.changeInputValueTo('a\nb'); expect($rootScope.list).toEqual(['a','b']); }); + + it('should support splitting on whitespace', function() { + helper.compileInput(''); + helper.changeInputValueTo('a b'); + expect($rootScope.list).toEqual(['a','b']); + }); }); }); diff --git a/test/ng/directive/ngModelOptionsSpec.js b/test/ng/directive/ngModelOptionsSpec.js new file mode 100644 index 000000000000..09a9ad5f4a7c --- /dev/null +++ b/test/ng/directive/ngModelOptionsSpec.js @@ -0,0 +1,940 @@ +'use strict'; + +/* globals + generateInputCompilerHelper: false, + defaultModelOptions: false + */ +describe('ngModelOptions', function() { + + describe('defaultModelOptions', function() { + it('should provide default values', function() { + expect(defaultModelOptions.getOption('updateOn')).toEqual(''); + expect(defaultModelOptions.getOption('updateOnDefault')).toEqual(true); + expect(defaultModelOptions.getOption('debounce')).toBe(0); + expect(defaultModelOptions.getOption('getterSetter')).toBe(false); + expect(defaultModelOptions.getOption('allowInvalid')).toBe(false); + expect(defaultModelOptions.getOption('timezone')).toBe(null); + }); + }); + + describe('directive', function() { + + describe('basic usage', function() { + + var helper = {}, $rootScope, $compile, $timeout, $q; + + generateInputCompilerHelper(helper); + + beforeEach(inject(function(_$compile_, _$rootScope_, _$timeout_, _$q_) { + $compile = _$compile_; + $rootScope = _$rootScope_; + $timeout = _$timeout_; + $q = _$q_; + })); + + + describe('should fall back to `defaultModelOptions`', function() { + it('if there is no `ngModelOptions` directive', function() { + var inputElm = helper.compileInput( + ''); + + var inputOptions = $rootScope.form.alias.$options; + expect(inputOptions.getOption('updateOn')).toEqual(defaultModelOptions.getOption('updateOn')); + expect(inputOptions.getOption('updateOnDefault')).toEqual(defaultModelOptions.getOption('updateOnDefault')); + expect(inputOptions.getOption('debounce')).toEqual(defaultModelOptions.getOption('debounce')); + expect(inputOptions.getOption('getterSetter')).toEqual(defaultModelOptions.getOption('getterSetter')); + expect(inputOptions.getOption('allowInvalid')).toEqual(defaultModelOptions.getOption('allowInvalid')); + expect(inputOptions.getOption('timezone')).toEqual(defaultModelOptions.getOption('timezone')); + }); + + + it('if `ngModelOptions` on the same element does not specify the option', function() { + var inputElm = helper.compileInput( + ''); + + var inputOptions = $rootScope.form.alias.$options; + expect(inputOptions.getOption('debounce')).toEqual(defaultModelOptions.getOption('debounce')); + expect(inputOptions.getOption('updateOnDefault')).toBe(false); + expect(inputOptions.getOption('updateOnDefault')).not.toEqual(defaultModelOptions.getOption('updateOnDefault')); + }); + + + it('if the first `ngModelOptions` ancestor does not specify the option', function() { + var form = $compile('
          ' + + '' + + '
          ')($rootScope); + var inputOptions = $rootScope.form.alias.$options; + + expect(inputOptions.getOption('debounce')).toEqual(defaultModelOptions.getOption('debounce')); + expect(inputOptions.getOption('updateOnDefault')).toBe(false); + expect(inputOptions.getOption('updateOnDefault')).not.toEqual(defaultModelOptions.getOption('updateOnDefault')); + dealoc(form); + }); + }); + + + describe('sharing and inheritance', function() { + + it('should not inherit options from ancestor `ngModelOptions` directives by default', function() { + var container = $compile( + '
          ' + + '
          ' + + '' + + '
          ' + + '
          ')($rootScope); + + var form = container.find('form'); + var input = container.find('input'); + + var containerOptions = container.controller('ngModelOptions').$options; + var formOptions = form.controller('ngModelOptions').$options; + var inputOptions = input.controller('ngModelOptions').$options; + + expect(containerOptions.getOption('allowInvalid')).toEqual(true); + expect(formOptions.getOption('allowInvalid')).toEqual(false); + expect(inputOptions.getOption('allowInvalid')).toEqual(false); + + expect(containerOptions.getOption('updateOn')).toEqual(''); + expect(containerOptions.getOption('updateOnDefault')).toEqual(true); + expect(formOptions.getOption('updateOn')).toEqual('blur'); + expect(formOptions.getOption('updateOnDefault')).toEqual(false); + expect(inputOptions.getOption('updateOn')).toEqual(''); + expect(inputOptions.getOption('updateOnDefault')).toEqual(true); + + dealoc(container); + }); + + it('should inherit options that are marked with "$inherit" from the nearest ancestor `ngModelOptions` directive', function() { + var container = $compile( + '
          ' + + '
          ' + + '' + + '
          ' + + '
          ')($rootScope); + + var form = container.find('form'); + var input = container.find('input'); + + var containerOptions = container.controller('ngModelOptions').$options; + var formOptions = form.controller('ngModelOptions').$options; + var inputOptions = input.controller('ngModelOptions').$options; + + expect(containerOptions.getOption('allowInvalid')).toEqual(true); + expect(formOptions.getOption('allowInvalid')).toEqual(true); + expect(inputOptions.getOption('allowInvalid')).toEqual(false); + + expect(containerOptions.getOption('updateOn')).toEqual(''); + expect(containerOptions.getOption('updateOnDefault')).toEqual(true); + expect(formOptions.getOption('updateOn')).toEqual('blur'); + expect(formOptions.getOption('updateOnDefault')).toEqual(false); + expect(inputOptions.getOption('updateOn')).toEqual(''); + expect(inputOptions.getOption('updateOnDefault')).toEqual(true); + + dealoc(container); + }); + + it('should inherit all unspecified options if the options object contains a `"*"` property with value "$inherit"', function() { + var container = $compile( + '
          ' + + '
          ' + + '' + + '
          ' + + '
          ')($rootScope); + + var form = container.find('form'); + var input = container.find('input'); + + var containerOptions = container.controller('ngModelOptions').$options; + var formOptions = form.controller('ngModelOptions').$options; + var inputOptions = input.controller('ngModelOptions').$options; + + expect(containerOptions.getOption('allowInvalid')).toEqual(true); + expect(formOptions.getOption('allowInvalid')).toEqual(true); + expect(inputOptions.getOption('allowInvalid')).toEqual(false); + + expect(containerOptions.getOption('debounce')).toEqual(100); + expect(formOptions.getOption('debounce')).toEqual(100); + expect(inputOptions.getOption('debounce')).toEqual(0); + + expect(containerOptions.getOption('updateOn')).toEqual('keyup'); + expect(containerOptions.getOption('updateOnDefault')).toEqual(false); + expect(formOptions.getOption('updateOn')).toEqual('blur'); + expect(formOptions.getOption('updateOnDefault')).toEqual(false); + expect(inputOptions.getOption('updateOn')).toEqual(''); + expect(inputOptions.getOption('updateOnDefault')).toEqual(true); + + dealoc(container); + }); + + it('should correctly inherit default and another specified event for `updateOn`', function() { + var container = $compile( + '
          ' + + '' + + '
          ')($rootScope); + + var input = container.find('input'); + var inputOptions = input.controller('ngModelOptions').$options; + + expect(inputOptions.getOption('updateOn')).toEqual('blur'); + expect(inputOptions.getOption('updateOnDefault')).toEqual(true); + + dealoc(container); + }); + + + it('should `updateOnDefault` as well if we have `updateOn: "$inherit"`', function() { + var container = $compile( + '
          ' + + '' + + '
          ' + + '' + + '
          ' + + '
          ')($rootScope); + + var input1 = container.find('input').eq(0); + var inputOptions1 = input1.controller('ngModelOptions').$options; + + expect(inputOptions1.getOption('updateOn')).toEqual('keyup'); + expect(inputOptions1.getOption('updateOnDefault')).toEqual(false); + + var input2 = container.find('input').eq(1); + var inputOptions2 = input2.controller('ngModelOptions').$options; + + expect(inputOptions2.getOption('updateOn')).toEqual('blur'); + expect(inputOptions2.getOption('updateOnDefault')).toEqual(true); + + dealoc(container); + }); + + + it('should make a copy of the options object', function() { + $rootScope.options = {updateOn: 'default'}; + var inputElm = helper.compileInput( + ''); + expect($rootScope.options).toEqual({updateOn: 'default'}); + expect($rootScope.form.alias.$options).not.toBe($rootScope.options); + }); + + it('should be retrieved from an ancestor element containing an `ngModelOptions` directive', function() { + var doc = $compile( + '
          ' + + '' + + '
          ')($rootScope); + $rootScope.$digest(); + + var inputElm = doc.find('input'); + helper.changeGivenInputTo(inputElm, 'a'); + expect($rootScope.name).toEqual(undefined); + browserTrigger(inputElm, 'blur'); + expect($rootScope.name).toBeUndefined(); + $timeout.flush(2000); + expect($rootScope.name).toBeUndefined(); + $timeout.flush(9000); + expect($rootScope.name).toEqual('a'); + dealoc(doc); + }); + + it('should allow sharing options between multiple inputs', function() { + $rootScope.options = {updateOn: 'default'}; + var inputElm = helper.compileInput( + '' + + ''); + + helper.changeGivenInputTo(inputElm.eq(0), 'a'); + helper.changeGivenInputTo(inputElm.eq(1), 'b'); + expect($rootScope.name1).toEqual('a'); + expect($rootScope.name2).toEqual('b'); + }); + }); + + + describe('updateOn', function() { + it('should allow overriding the model update trigger event on text inputs', function() { + var inputElm = helper.compileInput( + ''); + + helper.changeInputValueTo('a'); + expect($rootScope.name).toBeUndefined(); + browserTrigger(inputElm, 'blur'); + expect($rootScope.name).toEqual('a'); + }); + + + it('should not dirty the input if nothing was changed before updateOn trigger', function() { + var inputElm = helper.compileInput( + ''); + + browserTrigger(inputElm, 'blur'); + expect($rootScope.form.alias.$pristine).toBeTruthy(); + }); + + + it('should allow overriding the model update trigger event on text areas', function() { + var inputElm = helper.compileInput( + '', + button: '', + summary: '', + details: '
          ', + a: '' + }, function(tmpl) { + var element = $compile(tmpl)(scope); + expect(element.attr('role')).toBeUndefined(); + }); + }); + describe('aria-checked when disabled', function() { beforeEach(configAriaProvider({ ariaChecked: false @@ -143,16 +541,16 @@ describe('$aria', function() { beforeEach(injectScopeAndCompiler); it('should not attach aria-checked', function() { - compileInput("
          "); + compileElement('
          '); expect(element.attr('aria-checked')).toBeUndefined(); - compileInput("
          "); + compileElement('
          '); expect(element.attr('aria-checked')).toBeUndefined(); - compileInput("
          "); + compileElement('
          '); expect(element.attr('aria-checked')).toBeUndefined(); - compileInput("
          "); + compileElement('
          '); expect(element.attr('aria-checked')).toBeUndefined(); }); }); @@ -160,52 +558,46 @@ describe('$aria', function() { describe('aria-disabled', function() { beforeEach(injectScopeAndCompiler); - it('should attach itself to input elements', function() { - scope.$apply('val = false'); - compileInput(""); - expect(element.attr('aria-disabled')).toBe('false'); - + they('should not attach itself to native $prop controls', { + input: '', + textarea: '', + select: '', + button: '' + }, function(tmpl) { + var element = $compile(tmpl)(scope); scope.$apply('val = true'); - expect(element.attr('aria-disabled')).toBe('true'); + + expect(element.attr('disabled')).toBeDefined(); + expect(element.attr('aria-disabled')).toBeUndefined(); }); - it('should attach itself to textarea elements', function() { - scope.$apply('val = false'); - compileInput(''); + it('should attach itself to custom controls', function() { + compileElement('
          '); expect(element.attr('aria-disabled')).toBe('false'); scope.$apply('val = true'); expect(element.attr('aria-disabled')).toBe('true'); + }); - it('should attach itself to button elements', function() { - scope.$apply('val = false'); - compileInput(''); - expect(element.attr('aria-disabled')).toBe('false'); + it('should not attach itself if an aria-disabled attribute is already present', function() { + compileElement('
          '); - scope.$apply('val = true'); - expect(element.attr('aria-disabled')).toBe('true'); + expect(element.attr('aria-disabled')).toBe('userSetValue'); }); - it('should attach itself to select elements', function() { - scope.$apply('val = false'); - compileInput(''); - expect(element.attr('aria-disabled')).toBe('false'); - scope.$apply('val = true'); + it('should always set aria-disabled to a boolean value', function() { + compileElement('
          '); + + scope.$apply('val = "test angular"'); expect(element.attr('aria-disabled')).toBe('true'); - }); - it('should not attach itself if an aria-disabled attribute is already present', function() { - var element = [ - $compile("")(scope), - $compile("")(scope), - $compile("")(scope), - $compile("")(scope) - ]; + scope.$apply('val = null'); + expect(element.attr('aria-disabled')).toBe('false'); - scope.$apply('val = true'); - expectAriaAttrOnEachElement(element, 'aria-disabled', 'userSetValue'); + scope.$apply('val = {}'); + expect(element.attr('aria-disabled')).toBe('true'); }); }); @@ -216,15 +608,10 @@ describe('$aria', function() { beforeEach(injectScopeAndCompiler); it('should not attach aria-disabled', function() { - var element = [ - $compile("")(scope), - $compile("")(scope), - $compile("")(scope), - $compile("")(scope) - ]; + compileElement('
          '); - scope.$apply('val = false'); - expectAriaAttrOnEachElement(element, 'aria-disabled', undefined); + scope.$apply('val = true'); + expect(element.attr('aria-disabled')).toBeUndefined(); }); }); @@ -232,19 +619,43 @@ describe('$aria', function() { beforeEach(injectScopeAndCompiler); it('should attach aria-invalid to input', function() { - compileInput(''); - scope.$apply("txtInput='LTten'"); + compileElement(''); + scope.$apply('txtInput=\'LTten\''); + expect(element.attr('aria-invalid')).toBe('true'); + + scope.$apply('txtInput=\'morethantencharacters\''); + expect(element.attr('aria-invalid')).toBe('false'); + }); + + it('should attach aria-invalid to custom controls', function() { + compileElement('
          '); + scope.$apply('txtInput=\'LTten\''); expect(element.attr('aria-invalid')).toBe('true'); - scope.$apply("txtInput='morethantencharacters'"); + scope.$apply('txtInput=\'morethantencharacters\''); expect(element.attr('aria-invalid')).toBe('false'); }); it('should not attach itself if aria-invalid is already present', function() { - compileInput(''); - scope.$apply("txtInput='LTten'"); + compileElement(''); + scope.$apply('txtInput=\'LTten\''); expect(element.attr('aria-invalid')).toBe('userSetValue'); }); + + it('should not attach if input is type="hidden"', function() { + compileElement(''); + expect(element.attr('aria-invalid')).toBeUndefined(); + }); + + + it('should attach aria-invalid to custom control that is type="hidden"', function() { + compileElement('
          '); + scope.$apply('txtInput=\'LTten\''); + expect(element.attr('aria-invalid')).toBe('true'); + + scope.$apply('txtInput=\'morethantencharacters\''); + expect(element.attr('aria-invalid')).toBe('false'); + }); }); describe('aria-invalid when disabled', function() { @@ -254,116 +665,113 @@ describe('$aria', function() { beforeEach(injectScopeAndCompiler); it('should not attach aria-invalid if the option is disabled', function() { - scope.$apply("txtInput='LTten'"); - compileInput(''); + scope.$apply('txtInput=\'LTten\''); + compileElement(''); expect(element.attr('aria-invalid')).toBeUndefined(); }); }); - describe('aria-required', function() { + describe('aria-readonly', function() { beforeEach(injectScopeAndCompiler); - it('should attach aria-required to input', function() { - compileInput(''); - expect(element.attr('aria-required')).toBe('true'); + they('should not attach itself to native $prop controls', { + input: '', + textarea: '', + select: '', + button: '' + }, function(tmpl) { + var element = $compile(tmpl)(scope); + scope.$apply('val = true'); - scope.$apply("val='input is valid now'"); - expect(element.attr('aria-required')).toBe('false'); + expect(element.attr('readonly')).toBeDefined(); + expect(element.attr('aria-readonly')).toBeUndefined(); }); - it('should attach aria-required to textarea', function() { - compileInput(''); - expect(element.attr('aria-required')).toBe('true'); - - scope.$apply("val='input is valid now'"); - expect(element.attr('aria-required')).toBe('false'); - }); + it('should attach itself to custom controls', function() { + compileElement('
          '); + expect(element.attr('aria-readonly')).toBe('false'); - it('should attach aria-required to select', function() { - compileInput(''); - expect(element.attr('aria-required')).toBe('true'); + scope.$apply('val = true'); + expect(element.attr('aria-readonly')).toBe('true'); - scope.$apply("val='input is valid now'"); - expect(element.attr('aria-required')).toBe('false'); }); - it('should attach aria-required to ngRequired', function() { - compileInput(''); - expect(element.attr('aria-required')).toBe('true'); + it('should not attach itself if an aria-readonly attribute is already present', function() { + compileElement('
          '); - scope.$apply("val='input is valid now'"); - expect(element.attr('aria-required')).toBe('false'); + expect(element.attr('aria-readonly')).toBe('userSetValue'); }); - it('should not attach itself if aria-required is already present', function() { - compileInput(""); - expect(element.attr('aria-required')).toBe('userSetValue'); + it('should always set aria-readonly to a boolean value', function() { + compileElement('
          '); - compileInput(""); - expect(element.attr('aria-required')).toBe('userSetValue'); + scope.$apply('val = "test angular"'); + expect(element.attr('aria-readonly')).toBe('true'); - compileInput(""); - expect(element.attr('aria-required')).toBe('userSetValue'); + scope.$apply('val = null'); + expect(element.attr('aria-readonly')).toBe('false'); - compileInput(""); - expect(element.attr('aria-required')).toBe('userSetValue'); + scope.$apply('val = {}'); + expect(element.attr('aria-readonly')).toBe('true'); }); }); - describe('aria-required when disabled', function() { + describe('aria-readonly when disabled', function() { beforeEach(configAriaProvider({ - ariaRequired: false + ariaReadonly: false })); beforeEach(injectScopeAndCompiler); - it('should not add the aria-required attribute', function() { - compileInput(""); - expect(element.attr('aria-required')).toBeUndefined(); + it('should not add the aria-readonly attribute', function() { + compileElement(''); + expect(element.attr('aria-readonly')).toBeUndefined(); - compileInput(""); - expect(element.attr('aria-required')).toBeUndefined(); - - compileInput(""); - expect(element.attr('aria-required')).toBeUndefined(); + compileElement('
          '); + expect(element.attr('aria-readonly')).toBeUndefined(); }); }); - describe('aria-multiline', function() { + describe('aria-required', function() { beforeEach(injectScopeAndCompiler); - it('should attach itself to textarea', function() { - compileInput(''); - expect(element.attr('aria-multiline')).toBe('true'); + it('should not attach to input', function() { + compileElement(''); + expect(element.attr('aria-required')).toBeUndefined(); + }); + + it('should attach to custom controls with ngModel and required', function() { + compileElement('
          '); + expect(element.attr('aria-required')).toBe('true'); }); - it('should attach itself role="textbox"', function() { - compileInput('
          '); - expect(element.attr('aria-multiline')).toBe('true'); + it('should set aria-required to false when ng-required is false', function() { + compileElement('
          '); + expect(element.attr('aria-required')).toBe('false'); }); - it('should not attach itself if aria-multiline is already present', function() { - compileInput(''); - expect(element.attr('aria-multiline')).toBe('userSetValue'); + it('should attach to custom controls with ngRequired', function() { + compileElement('
          '); + expect(element.attr('aria-required')).toBe('true'); + }); - compileInput('
          '); - expect(element.attr('aria-multiline')).toBe('userSetValue'); + it('should not attach itself if aria-required is already present', function() { + compileElement('
          '); + expect(element.attr('aria-required')).toBe('userSetValue'); }); }); - describe('aria-multiline when disabled', function() { + describe('aria-required when disabled', function() { beforeEach(configAriaProvider({ - ariaMultiline: false + ariaRequired: false })); beforeEach(injectScopeAndCompiler); - it('should not attach itself to textarea', function() { - compileInput(''); - expect(element.attr('aria-multiline')).toBeUndefined(); - }); + it('should not add the aria-required attribute', function() { + compileElement(''); + expect(element.attr('aria-required')).toBeUndefined(); - it('should not attach itself role="textbox"', function() { - compileInput('
          '); - expect(element.attr('aria-multiline')).toBeUndefined(); + compileElement('
          '); + expect(element.attr('aria-required')).toBeUndefined(); }); }); @@ -378,12 +786,12 @@ describe('$aria', function() { ]; scope.$apply('val = 50'); - expectAriaAttrOnEachElement(element, 'aria-valuenow', "50"); - expectAriaAttrOnEachElement(element, 'aria-valuemin', "0"); - expectAriaAttrOnEachElement(element, 'aria-valuemax', "100"); + expectAriaAttrOnEachElement(element, 'aria-valuenow', '50'); + expectAriaAttrOnEachElement(element, 'aria-valuemin', '0'); + expectAriaAttrOnEachElement(element, 'aria-valuemax', '100'); scope.$apply('val = 90'); - expectAriaAttrOnEachElement(element, 'aria-valuenow', "90"); + expectAriaAttrOnEachElement(element, 'aria-valuenow', '90'); }); it('should not attach if aria-value* is already present', function() { @@ -398,6 +806,36 @@ describe('$aria', function() { expectAriaAttrOnEachElement(element, 'aria-valuemin', 'userSetValue2'); expectAriaAttrOnEachElement(element, 'aria-valuemax', 'userSetValue3'); }); + + + it('should update `aria-valuemin/max` when `min/max` changes dynamically', function() { + scope.$apply('min = 25; max = 75'); + compileElement(''); + + expect(element.attr('aria-valuemin')).toBe('25'); + expect(element.attr('aria-valuemax')).toBe('75'); + + scope.$apply('min = 0'); + expect(element.attr('aria-valuemin')).toBe('0'); + + scope.$apply('max = 100'); + expect(element.attr('aria-valuemax')).toBe('100'); + }); + + + it('should update `aria-valuemin/max` when `ng-min/ng-max` changes dynamically', function() { + scope.$apply('min = 25; max = 75'); + compileElement(''); + + expect(element.attr('aria-valuemin')).toBe('25'); + expect(element.attr('aria-valuemax')).toBe('75'); + + scope.$apply('min = 0'); + expect(element.attr('aria-valuemin')).toBe('0'); + + scope.$apply('max = 100'); + expect(element.attr('aria-valuemax')).toBe('100'); + }); }); describe('announcing ngMessages', function() { @@ -407,7 +845,7 @@ describe('$aria', function() { var element = [ $compile('
          ')(scope) ]; - expectAriaAttrOnEachElement(element, 'aria-live', "assertive"); + expectAriaAttrOnEachElement(element, 'aria-live', 'assertive'); }); }); @@ -420,12 +858,12 @@ describe('$aria', function() { it('should not attach itself', function() { scope.$apply('val = 50'); - compileInput(''); + compileElement(''); expect(element.attr('aria-valuenow')).toBeUndefined(); expect(element.attr('aria-valuemin')).toBeUndefined(); expect(element.attr('aria-valuemax')).toBeUndefined(); - compileInput('
          '); + compileElement('
          '); expect(element.attr('aria-valuenow')).toBeUndefined(); expect(element.attr('aria-valuemin')).toBeUndefined(); expect(element.attr('aria-valuemax')).toBeUndefined(); @@ -435,114 +873,305 @@ describe('$aria', function() { describe('tabindex', function() { beforeEach(injectScopeAndCompiler); - it('should attach tabindex to role="checkbox", ng-click, and ng-dblclick', function() { - compileInput('
          '); + they('should not attach to native control $prop', { + 'button': '', + 'a': '', + 'input[text]': '', + 'input[radio]': '', + 'input[checkbox]': '', + 'textarea': '', + 'select': '', + 'details': '
          ' + }, function(html) { + compileElement(html); + expect(element.attr('tabindex')).toBeUndefined(); + }); + + it('should not attach to random ng-model elements', function() { + compileElement('
          '); + expect(element.attr('tabindex')).toBeUndefined(); + }); + + it('should attach tabindex to custom inputs', function() { + compileElement('
          '); expect(element.attr('tabindex')).toBe('0'); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBe('0'); + }); - compileInput('
          '); + it('should attach to ng-click and ng-dblclick', function() { + compileElement('
          '); + expect(element.attr('tabindex')).toBe('0'); + + compileElement('
          '); expect(element.attr('tabindex')).toBe('0'); }); it('should not attach tabindex if it is already on an element', function() { - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBe('userSetValue'); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBe('userSetValue'); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBe('userSetValue'); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBe('userSetValue'); }); - - it('should set proper tabindex values for radiogroup', function() { - compileInput('
          ' + - '
          1
          ' + - '
          2
          ' + - '
          '); - - var one = element.contents().eq(0); - var two = element.contents().eq(1); - - scope.$apply("val = 'one'"); - expect(one.attr('tabindex')).toBe('0'); - expect(two.attr('tabindex')).toBe('-1'); - - scope.$apply("val = 'two'"); - expect(one.attr('tabindex')).toBe('-1'); - expect(two.attr('tabindex')).toBe('0'); - - dealoc(element); - }); }); describe('accessible actions', function() { + var clickEvents; + beforeEach(injectScopeAndCompiler); + beforeEach(function() { + clickEvents = []; + scope.onClick = jasmine.createSpy('onClick').and.callFake(function(evt) { + var nodeName = evt ? evt.target.nodeName.toLowerCase() : ''; + var prevented = !!(evt && evt.isDefaultPrevented()); + clickEvents.push(nodeName + '(' + prevented + ')'); + }); + }); - var clickFn; + it('should trigger a click from the keyboard (and prevent default action)', function() { + compileElement( + '
          ' + + '
          ' + + '
          ' + + '
          '); - it('should a trigger click from the keyboard', function() { - scope.someAction = function() {}; - compileInput('
          '); - clickFn = spyOn(scope, 'someAction'); + var divElement = element.find('div'); + var liElement = element.find('li'); - element.triggerHandler({type: 'keypress', keyCode: 32}); + divElement.triggerHandler({type: 'keydown', keyCode: 13}); + liElement.triggerHandler({type: 'keydown', keyCode: 13}); + divElement.triggerHandler({type: 'keydown', keyCode: 32}); + liElement.triggerHandler({type: 'keydown', keyCode: 32}); - expect(clickFn).toHaveBeenCalled(); + expect(clickEvents).toEqual(['div(true)', 'li(true)', 'div(true)', 'li(true)']); }); - it('should not override existing ng-keypress', function() { - scope.someOtherAction = function() {}; - var keypressFn = spyOn(scope, 'someOtherAction'); + it('should trigger a click in browsers that provide `event.which` instead of `event.keyCode`', + function() { + compileElement( + '
          ' + + '
          ' + + '
          ' + + '
          '); + + var divElement = element.find('div'); + var liElement = element.find('li'); + + divElement.triggerHandler({type: 'keydown', which: 13}); + liElement.triggerHandler({type: 'keydown', which: 13}); + divElement.triggerHandler({type: 'keydown', which: 32}); + liElement.triggerHandler({type: 'keydown', which: 32}); + + expect(clickEvents).toEqual(['div(true)', 'li(true)', 'div(true)', 'li(true)']); + } + ); + + it('should not prevent default keyboard action if the target element has editable content', + inject(function($document) { + // Note: + // `contenteditable` is an enumarated (not a boolean) attribute (see + // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable). + // We need to check the following conditions: + // - No attribute. + // - Value: "" + // - Value: "true" + // - Value: "false" + + function eventFor(keyCode) { + return {bubbles: true, cancelable: true, keyCode: keyCode}; + } + + compileElement( + '
          ' + + // No attribute. + '
          ' + + '
          ' + + '
          ' + + '
          ' + + + // Value: "" + '
          ' + + '
          ' + + '
          ' + + '
          ' + + + // Value: "true" + '
          ' + + '
          ' + + '
          ' + + '
          ' + + + // Value: "false" + '
          ' + + '
          ' + + '
          ' + + '
          ' + + '
          '); + + // Support: Safari 11-12+ + // Attach to DOM, because otherwise Safari will not update the `isContentEditable` property + // based on the `contenteditable` attribute. + $document.find('body').append(element); + + var containers = element.children(); + var container; + + // Using `browserTrigger()`, because it supports event bubbling. + + // No attribute | Elements are not editable. + container = containers.eq(0); + browserTrigger(container.find('div'), 'keydown', eventFor(13)); + browserTrigger(container.find('ul'), 'keydown', eventFor(32)); + browserTrigger(container.find('li'), 'keydown', eventFor(13)); + + expect(clickEvents).toEqual(['div(true)', 'ul(true)', 'li(true)']); + + // Value: "" | Elements are editable. + clickEvents = []; + container = containers.eq(1); + browserTrigger(container.find('div'), 'keydown', eventFor(32)); + browserTrigger(container.find('ul'), 'keydown', eventFor(13)); + browserTrigger(container.find('li'), 'keydown', eventFor(32)); + + expect(clickEvents).toEqual(['div(false)', 'ul(true)', 'li(false)']); + + // Value: "true" | Elements are editable. + clickEvents = []; + container = containers.eq(2); + browserTrigger(container.find('div'), 'keydown', eventFor(13)); + browserTrigger(container.find('ul'), 'keydown', eventFor(32)); + browserTrigger(container.find('li'), 'keydown', eventFor(13)); + + expect(clickEvents).toEqual(['div(false)', 'ul(true)', 'li(false)']); + + // Value: "false" | Elements are not editable. + clickEvents = []; + container = containers.eq(3); + browserTrigger(container.find('div'), 'keydown', eventFor(32)); + browserTrigger(container.find('ul'), 'keydown', eventFor(13)); + browserTrigger(container.find('li'), 'keydown', eventFor(32)); + + expect(clickEvents).toEqual(['div(true)', 'ul(true)', 'li(true)']); + }) + ); + + they('should not prevent default keyboard action if an interactive $type element' + + 'is nested inside ng-click', nativeAriaNodeNames, function(elementType) { + function createHTML(type) { + return '<' + type + '>'; + } + + compileElement( + '
          ' + + '
          ' + createHTML(elementType) + '
          ' + + '
          '); + + var divElement = element.find('div'); + var interactiveElement = element.find(elementType); + + // Use browserTrigger because it supports event bubbling + // 13 Enter + browserTrigger(interactiveElement, 'keydown', {cancelable: true, bubbles: true, keyCode: 13}); + expect(clickEvents).toEqual([elementType.toLowerCase() + '(false)']); + + clickEvents = []; + + // 32 Space + browserTrigger(interactiveElement, 'keydown', {cancelable: true, bubbles: true, keyCode: 32}); + expect(clickEvents).toEqual([elementType.toLowerCase() + '(false)']); + } + ); + + they('should not bind to key events if there is existing `ng-$prop`', + ['keydown', 'keypress', 'keyup'], function(eventName) { + scope.onKeyEvent = jasmine.createSpy('onKeyEvent'); + compileElement('
          '); + + element.triggerHandler({type: eventName, keyCode: 13}); + element.triggerHandler({type: eventName, keyCode: 32}); + + expect(scope.onClick).not.toHaveBeenCalled(); + expect(scope.onKeyEvent).toHaveBeenCalledTimes(2); + } + ); + + it('should update bindings when keydown is handled', function() { + scope.count = 0; + compileElement('
          Count: {{ count }}
          '); + + expect(element.text()).toBe('Count: 0'); + + element.triggerHandler({type: 'keydown', keyCode: 13}); + expect(element.text()).toBe('Count: 1'); + + element.triggerHandler({type: 'keydown', keyCode: 32}); + expect(element.text()).toBe('Count: 2'); + }); - scope.someAction = function() {}; - clickFn = spyOn(scope, 'someAction'); - compileInput('
          '); + it('should pass `$event` to `ng-click` handler as local', function() { + compileElement('
          {{ event.type }}{{ event.keyCode }}
          '); + expect(element.text()).toBe(''); - element.triggerHandler({type: 'keypress', keyCode: 32}); + element.triggerHandler({type: 'keydown', keyCode: 13}); + expect(element.text()).toBe('keydown13'); - expect(clickFn).not.toHaveBeenCalled(); - expect(keypressFn).toHaveBeenCalled(); + element.triggerHandler({type: 'keydown', keyCode: 32}); + expect(element.text()).toBe('keydown32'); }); - it('should update bindings when keypress handled', function() { - compileInput('
          {{text}}
          '); - expect(element.text()).toBe(''); - spyOn(scope.$root, '$digest').andCallThrough(); - element.triggerHandler({ type: 'keypress', keyCode: 13 }); - expect(element.text()).toBe('clicked!'); - expect(scope.$root.$digest).toHaveBeenCalledOnce(); + it('should not bind keydown to natively interactive elements', function() { + compileElement(''); + + element.triggerHandler({type: 'keydown', keyCode: 13}); + element.triggerHandler({type: 'keydown', keyCode: 32}); + + expect(scope.onClick).not.toHaveBeenCalled(); }); + }); - it('should pass $event to ng-click handler as local', function() { - compileInput('
          {{event.type}}' + - '{{event.keyCode}}
          '); - expect(element.text()).toBe(''); - element.triggerHandler({ type: 'keypress', keyCode: 13 }); - expect(element.text()).toBe('keypress13'); + describe('actions when bindRoleForClick is set to false', function() { + beforeEach(configAriaProvider({ + bindRoleForClick: false + })); + beforeEach(injectScopeAndCompiler); + + it('should not add a button role', function() { + compileElement(''); + expect(element.attr('role')).toBeUndefined(); }); }); - describe('actions when bindKeypress set to false', function() { + describe('actions when bindKeydown is set to false', function() { beforeEach(configAriaProvider({ - bindKeypress: false + bindKeydown: false })); beforeEach(injectScopeAndCompiler); - it('should not a trigger click from the keyboard', function() { - scope.someAction = function() {}; - var clickFn = spyOn(scope, 'someAction'); + it('should not trigger click', function() { + scope.someAction = jasmine.createSpy('someAction'); - element = $compile('
          >
          ')(scope); + element = $compile('
          ')(scope); + element.triggerHandler({type: 'keydown', keyCode: 13}); + element.triggerHandler({type: 'keydown', keyCode: 32}); + element.triggerHandler({type: 'keypress', keyCode: 13}); element.triggerHandler({type: 'keypress', keyCode: 32}); + element.triggerHandler({type: 'keyup', keyCode: 13}); + element.triggerHandler({type: 'keyup', keyCode: 32}); - expect(clickFn).not.toHaveBeenCalled(); + expect(scope.someAction).not.toHaveBeenCalled(); + + element.triggerHandler({type: 'click', keyCode: 32}); + + expect(scope.someAction).toHaveBeenCalledOnce(); }); }); @@ -553,32 +1182,66 @@ describe('$aria', function() { beforeEach(injectScopeAndCompiler); it('should not add a tabindex attribute', function() { - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBeUndefined(); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBeUndefined(); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBeUndefined(); - compileInput('
          '); + compileElement('
          '); expect(element.attr('tabindex')).toBeUndefined(); }); }); -}); -function expectAriaAttrOnEachElement(elem, ariaAttr, expected) { - angular.forEach(elem, function(val) { - expect(angular.element(val).attr(ariaAttr)).toBe(expected); + describe('ngModel', function() { + it('should not break when manually compiling', function() { + module(function($compileProvider) { + $compileProvider.directive('foo', function() { + return { + priority: 10, + terminal: true, + link: function(scope, elem) { + $compile(elem, null, 10)(scope); + } + }; + }); + }); + + injectScopeAndCompiler(); + compileElement('
          '); + + // Just check an arbitrary feature to make sure it worked + expect(element.attr('tabindex')).toBe('0'); + }); }); -} -function configAriaProvider(config) { - return function() { - angular.module('ariaTest', ['ngAria']).config(function($ariaProvider) { - $ariaProvider.config(config); + // Helpers + function compileElement(inputHtml) { + element = $compile(inputHtml)(scope); + scope.$digest(); + } + + function configAriaProvider(config) { + return function() { + module(function($ariaProvider) { + $ariaProvider.config(config); + }); + }; + } + + function expectAriaAttrOnEachElement(elem, ariaAttr, expected) { + angular.forEach(elem, function(val) { + expect(angular.element(val).attr(ariaAttr)).toBe(expected); + }); + } + + function injectScopeAndCompiler() { + return inject(function(_$compile_, _$rootScope_) { + $compile = _$compile_; + scope = _$rootScope_; }); - module('ariaTest'); - }; -} + } +}); diff --git a/test/ngCookies/cookieWriterSpec.js b/test/ngCookies/cookieWriterSpec.js new file mode 100644 index 000000000000..71325b0a70bc --- /dev/null +++ b/test/ngCookies/cookieWriterSpec.js @@ -0,0 +1,210 @@ +'use strict'; + +describe('$$cookieWriter', function() { + var $$cookieWriter, document; + + function deleteAllCookies() { + var cookies = document.cookie.split(';'); + var path = window.location.pathname; + + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i]; + var eqPos = cookie.indexOf('='); + var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie; + var parts = path.split('/'); + while (parts.length) { + document.cookie = name + '=;path=' + (parts.join('/') || '/') + ';expires=Thu, 01 Jan 1970 00:00:00 GMT'; + parts.pop(); + } + } + } + + beforeEach(function() { + document = window.document; + deleteAllCookies(); + expect(document.cookie).toEqual(''); + + module('ngCookies'); + inject(function(_$$cookieWriter_) { + $$cookieWriter = _$$cookieWriter_; + }); + }); + + + afterEach(function() { + deleteAllCookies(); + expect(document.cookie).toEqual(''); + }); + + + describe('remove via $$cookieWriter(cookieName, undefined)', function() { + + it('should remove a cookie when it is present', function() { + document.cookie = 'foo=bar;path=/'; + + $$cookieWriter('foo', undefined); + + expect(document.cookie).toEqual(''); + }); + + + it('should do nothing when an nonexisting cookie is being removed', function() { + $$cookieWriter('doesntexist', undefined); + expect(document.cookie).toEqual(''); + }); + }); + + + describe('put via $$cookieWriter(cookieName, string)', function() { + + it('should create and store a cookie', function() { + $$cookieWriter('cookieName', 'cookie=Value'); + expect(document.cookie).toMatch(/cookieName=cookie%3DValue;? ?/); + }); + + + it('should overwrite an existing unsynced cookie', function() { + document.cookie = 'cookie=new;path=/'; + + var oldVal = $$cookieWriter('cookie', 'newer'); + + expect(document.cookie).toEqual('cookie=newer'); + expect(oldVal).not.toBeDefined(); + }); + + it('should encode both name and value', function() { + $$cookieWriter('cookie1=', 'val;ue'); + $$cookieWriter('cookie2=bar;baz', 'val=ue'); + + var rawCookies = document.cookie.split('; '); //order is not guaranteed, so we need to parse + expect(rawCookies.length).toEqual(2); + expect(rawCookies).toContain('cookie1%3D=val%3Bue'); + expect(rawCookies).toContain('cookie2%3Dbar%3Bbaz=val%3Due'); + }); + + it('should log warnings when 4kb per cookie storage limit is reached', inject(function($log) { + var i, longVal = '', cookieStr; + + for (i = 0; i < 4083; i++) { + longVal += 'x'; + } + + cookieStr = document.cookie; + $$cookieWriter('x', longVal); //total size 4093-4096, so it should go through + expect(document.cookie).not.toEqual(cookieStr); + expect(document.cookie).toEqual('x=' + longVal); + expect($log.warn.logs).toEqual([]); + + $$cookieWriter('x', longVal + 'xxxx'); //total size 4097-4099, a warning should be logged + expect($log.warn.logs).toEqual( + [['Cookie \'x\' possibly not set or overflowed because it was too large (4097 > 4096 ' + + 'bytes)!']]); + + //force browser to dropped a cookie and make sure that the cache is not out of sync + $$cookieWriter('x', 'shortVal'); + expect(document.cookie).toEqual('x=shortVal'); //needed to prime the cache + cookieStr = document.cookie; + $$cookieWriter('x', longVal + longVal + longVal); //should be too long for all browsers + + if (document.cookie !== cookieStr) { + this.fail(new Error('browser didn\'t drop long cookie when it was expected. make the ' + + 'cookie in this test longer')); + } + + expect(document.cookie).toEqual('x=shortVal'); + $log.reset(); + })); + }); + + describe('put via $$cookieWriter(cookieName, string), if no ', function() { + beforeEach(inject(function($browser) { + $browser.$$baseHref = undefined; + })); + + it('should default path in cookie to "" (empty string)', function() { + $$cookieWriter('cookie', 'bender'); + // This only fails in Safari and IE when cookiePath returns undefined + // Where it now succeeds since baseHref return '' instead of undefined + expect(document.cookie).toEqual('cookie=bender'); + }); + }); +}); + +describe('cookie options', function() { + var fakeDocument, $$cookieWriter; + var isUndefined = angular.isUndefined; + + function getLastCookieAssignment(key) { + return fakeDocument[0].cookie + .split(';') + .reduce(function(prev, value) { + var pair = value.split('=', 2); + if (pair[0] === key) { + if (isUndefined(prev)) { + return isUndefined(pair[1]) ? true : pair[1]; + } else { + throw new Error('duplicate key in cookie string'); + } + } else { + return prev; + } + }, undefined); + } + + beforeEach(function() { + fakeDocument = [{cookie: ''}]; + module('ngCookies', {$document: fakeDocument}); + inject(function($browser) { + $browser.$$baseHref = '/a/b'; + }); + inject(function(_$$cookieWriter_) { + $$cookieWriter = _$$cookieWriter_; + }); + }); + + it('should use baseHref as default path', function() { + $$cookieWriter('name', 'value'); + expect(getLastCookieAssignment('path')).toBe('/a/b'); + }); + + it('should accept path option', function() { + $$cookieWriter('name', 'value', {path: '/c/d'}); + expect(getLastCookieAssignment('path')).toBe('/c/d'); + }); + + it('should accept domain option', function() { + $$cookieWriter('name', 'value', {domain: '.example.com'}); + expect(getLastCookieAssignment('domain')).toBe('.example.com'); + }); + + it('should accept secure option', function() { + $$cookieWriter('name', 'value', {secure: true}); + expect(getLastCookieAssignment('secure')).toBe(true); + }); + + it('should accept samesite option when value is lax', function() { + $$cookieWriter('name', 'value', {samesite: 'lax'}); + expect(getLastCookieAssignment('samesite')).toBe('lax'); + }); + + it('should accept samesite option when value is strict', function() { + $$cookieWriter('name', 'value', {samesite: 'strict'}); + expect(getLastCookieAssignment('samesite')).toBe('strict'); + }); + + it('should accept expires option on set', function() { + $$cookieWriter('name', 'value', {expires: 'Fri, 19 Dec 2014 00:00:00 GMT'}); + expect(getLastCookieAssignment('expires')).toMatch(/^Fri, 19 Dec 2014 00:00:00 (UTC|GMT)$/); + }); + + it('should always use epoch time as expire time on remove', function() { + $$cookieWriter('name', undefined, {expires: 'Fri, 19 Dec 2014 00:00:00 GMT'}); + expect(getLastCookieAssignment('expires')).toMatch(/^Thu, 0?1 Jan 1970 00:00:00 (UTC|GMT)$/); + }); + + it('should accept date object as expires option', function() { + $$cookieWriter('name', 'value', {expires: new Date(Date.UTC(1981, 11, 27))}); + expect(getLastCookieAssignment('expires')).toMatch(/^Sun, 27 Dec 1981 00:00:00 (UTC|GMT)$/); + }); + +}); diff --git a/test/ngCookies/cookiesSpec.js b/test/ngCookies/cookiesSpec.js index 0a36c92f2aef..38c030443c3f 100644 --- a/test/ngCookies/cookiesSpec.js +++ b/test/ngCookies/cookiesSpec.js @@ -1,149 +1,142 @@ 'use strict'; describe('$cookies', function() { - beforeEach(module('ngCookies', function($provide) { - $provide.factory('$browser', function() { - return angular.extend(new angular.mock.$Browser(), {cookieHash: {preexisting:'oldCookie'}}); + var mockedCookies; + + beforeEach(function() { + mockedCookies = {}; + module('ngCookies', { + $$cookieWriter: jasmine.createSpy('$$cookieWriter').and.callFake(function(name, value) { + mockedCookies[name] = value; + }), + $$cookieReader: function() { + return mockedCookies; + } }); - })); + }); - it('should provide access to existing cookies via object properties and keep them in sync', - inject(function($cookies, $browser, $rootScope) { - expect($cookies).toEqual({'preexisting': 'oldCookie'}); + it('should serialize objects to json', inject(function($cookies) { + $cookies.putObject('objectCookie', {id: 123, name: 'blah'}); + expect($cookies.get('objectCookie')).toEqual('{"id":123,"name":"blah"}'); + })); - // access internal cookie storage of the browser mock directly to simulate behavior of - // document.cookie - $browser.cookieHash['brandNew'] = 'cookie'; - $browser.poll(); - expect($cookies).toEqual({'preexisting': 'oldCookie', 'brandNew':'cookie'}); + it('should deserialize json to object', inject(function($cookies) { + $cookies.put('objectCookie', '{"id":123,"name":"blah"}'); + expect($cookies.getObject('objectCookie')).toEqual({id: 123, name: 'blah'}); + })); - $browser.cookieHash['brandNew'] = 'cookie2'; - $browser.poll(); - expect($cookies).toEqual({'preexisting': 'oldCookie', 'brandNew':'cookie2'}); - delete $browser.cookieHash['brandNew']; - $browser.poll(); - expect($cookies).toEqual({'preexisting': 'oldCookie'}); + it('should delete objects from the store when remove is called', inject(function($cookies) { + $cookies.putObject('gonner', { 'I\'ll':'Be Back'}); + expect($cookies.get('gonner')).toEqual('{"I\'ll":"Be Back"}'); + $cookies.remove('gonner'); + expect($cookies.get('gonner')).toEqual(undefined); })); - it('should create or update a cookie when a value is assigned to a property', - inject(function($cookies, $browser, $rootScope) { - $cookies.oatmealCookie = 'nom nom'; - $rootScope.$digest(); - - expect($browser.cookies()). - toEqual({'preexisting': 'oldCookie', 'oatmealCookie':'nom nom'}); + it('should handle empty string value cookies', inject(function($cookies) { + $cookies.putObject('emptyCookie',''); + expect($cookies.get('emptyCookie')).toEqual('""'); + expect($cookies.getObject('emptyCookie')).toEqual(''); + mockedCookies['blankCookie'] = ''; + expect($cookies.getObject('blankCookie')).toEqual(''); + })); - $cookies.oatmealCookie = 'gone'; - $rootScope.$digest(); - expect($browser.cookies()). - toEqual({'preexisting': 'oldCookie', 'oatmealCookie': 'gone'}); + it('should put cookie value without serializing', inject(function($cookies) { + $cookies.put('name', 'value'); + $cookies.put('name2', '"value2"'); + expect($cookies.get('name')).toEqual('value'); + expect($cookies.getObject('name2')).toEqual('value2'); })); - it('should convert non-string values to string', - inject(function($cookies, $browser, $rootScope) { - $cookies.nonString = [1, 2, 3]; - $cookies.nullVal = null; - $cookies.undefVal = undefined; - var preexisting = $cookies.preexisting = function() {}; - $rootScope.$digest(); - expect($browser.cookies()).toEqual({ - 'preexisting': '' + preexisting, - 'nonString': '1,2,3', - 'nullVal': 'null', - 'undefVal': 'undefined' - }); - expect($cookies).toEqual({ - 'preexisting': '' + preexisting, - 'nonString': '1,2,3', - 'nullVal': 'null', - 'undefVal': 'undefined' - }); + it('should get cookie value without deserializing', inject(function($cookies) { + $cookies.put('name', 'value'); + $cookies.putObject('name2', 'value2'); + expect($cookies.get('name')).toEqual('value'); + expect($cookies.get('name2')).toEqual('"value2"'); })); + it('should get all the cookies', inject(function($cookies) { + $cookies.put('name', 'value'); + $cookies.putObject('name2', 'value2'); + expect($cookies.getAll()).toEqual({name: 'value', name2: '"value2"'}); + })); - it('should remove a cookie when a $cookies property is deleted', - inject(function($cookies, $browser, $rootScope) { - $cookies.oatmealCookie = 'nom nom'; - $rootScope.$digest(); - $browser.poll(); - expect($browser.cookies()). - toEqual({'preexisting': 'oldCookie', 'oatmealCookie':'nom nom'}); - - delete $cookies.oatmealCookie; - $rootScope.$digest(); - expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'}); + it('should pass options on put', inject(function($cookies, $$cookieWriter) { + $cookies.put('name', 'value', {path: '/a/b'}); + expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {path: '/a/b'}); })); - it('should drop or reset cookies that browser refused to store', - inject(function($cookies, $browser, $rootScope) { - var i, longVal; + it('should pass options on putObject', inject(function($cookies, $$cookieWriter) { + $cookies.putObject('name', 'value', {path: '/a/b'}); + expect($$cookieWriter).toHaveBeenCalledWith('name', '"value"', {path: '/a/b'}); + })); - for (i = 0; i < 5000; i++) { - longVal += '*'; - } - //drop if no previous value - $cookies.longCookie = longVal; - $rootScope.$digest(); - expect($cookies).toEqual({'preexisting': 'oldCookie'}); + it('should pass options on remove', inject(function($cookies, $$cookieWriter) { + $cookies.remove('name', {path: '/a/b'}); + expect($$cookieWriter).toHaveBeenCalledWith('name', undefined, {path: '/a/b'}); + })); - //reset if previous value existed - $cookies.longCookie = 'shortVal'; - $rootScope.$digest(); - expect($cookies).toEqual({'preexisting': 'oldCookie', 'longCookie': 'shortVal'}); - $cookies.longCookie = longVal; - $rootScope.$digest(); - expect($cookies).toEqual({'preexisting': 'oldCookie', 'longCookie': 'shortVal'}); - })); -}); + it('should pass default options on put', function() { + module(function($cookiesProvider) { + $cookiesProvider.defaults.secure = true; + }); + inject(function($cookies, $$cookieWriter) { + $cookies.put('name', 'value', {path: '/a/b'}); + expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {path: '/a/b', secure: true}); + }); + }); -describe('$cookieStore', function() { + it('should pass default options on putObject', function() { + module(function($cookiesProvider) { + $cookiesProvider.defaults.secure = true; + }); + inject(function($cookies, $$cookieWriter) { + $cookies.putObject('name', 'value', {path: '/a/b'}); + expect($$cookieWriter).toHaveBeenCalledWith('name', '"value"', {path: '/a/b', secure: true}); + }); + }); - beforeEach(module('ngCookies')); - it('should serialize objects to json', inject(function($cookieStore, $browser, $rootScope) { - $cookieStore.put('objectCookie', {id: 123, name: 'blah'}); - $rootScope.$digest(); - expect($browser.cookies()).toEqual({'objectCookie': '{"id":123,"name":"blah"}'}); - })); + it('should pass default options on remove', function() { + module(function($cookiesProvider) { + $cookiesProvider.defaults.secure = true; + }); + inject(function($cookies, $$cookieWriter) { + $cookies.remove('name', {path: '/a/b'}); + expect($$cookieWriter).toHaveBeenCalledWith('name', undefined, {path: '/a/b', secure: true}); + }); + }); - it('should deserialize json to object', inject(function($cookieStore, $browser) { - $browser.cookies('objectCookie', '{"id":123,"name":"blah"}'); - $browser.poll(); - expect($cookieStore.get('objectCookie')).toEqual({id: 123, name: 'blah'}); - })); + it('should let passed options override default options', function() { + module(function($cookiesProvider) { + $cookiesProvider.defaults.secure = true; + }); + inject(function($cookies, $$cookieWriter) { + $cookies.put('name', 'value', {secure: false}); + expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {secure: false}); + }); + }); - it('should delete objects from the store when remove is called', inject(function($cookieStore, $browser, $rootScope) { - $cookieStore.put('gonner', { "I'll":"Be Back"}); - $rootScope.$digest(); //force eval in test - $browser.poll(); - expect($browser.cookies()).toEqual({'gonner': '{"I\'ll":"Be Back"}'}); + it('should pass default options if no options are passed', function() { + module(function($cookiesProvider) { + $cookiesProvider.defaults.secure = true; + }); + inject(function($cookies, $$cookieWriter) { + $cookies.put('name', 'value'); + expect($$cookieWriter).toHaveBeenCalledWith('name', 'value', {secure: true}); + }); + }); - $cookieStore.remove('gonner'); - $rootScope.$digest(); - expect($browser.cookies()).toEqual({}); - })); - it('should handle empty string value cookies', inject(function($cookieStore, $browser, $rootScope) { - $cookieStore.put("emptyCookie",''); - $rootScope.$digest(); - expect($browser.cookies()). - toEqual({ 'emptyCookie': '""' }); - expect($cookieStore.get("emptyCookie")).toEqual(''); - - $browser.cookieHash['blankCookie'] = ''; - $browser.poll(); - expect($cookieStore.get("blankCookie")).toEqual(''); - })); -}); + }); diff --git a/test/ngMessageFormat/messageFormatSpec.js b/test/ngMessageFormat/messageFormatSpec.js new file mode 100644 index 000000000000..9ba14c8afa8d --- /dev/null +++ b/test/ngMessageFormat/messageFormatSpec.js @@ -0,0 +1,731 @@ +'use strict'; + +/* TODO: Add tests for: + • Whitespace preservation in messages. + • Whitespace ignored around syntax except for offset:N. + • Escaping for curlies and the # symbol. + • # symbol value. + • # symbol value when gender is nested inside plural. + • Error with nested # symbol. + • parser error messages. + • caching. + • watched expressions. + • test parsing AngularJS expressions + • test the different regexes + • test the different starting rules +*/ + +describe('$$ngMessageFormat', function() { + describe('core', function() { + var $$messageFormat, $parse, $interpolate, $locale, $rootScope; + + function Person(name, gender) { + this.name = name; + this.gender = gender; + } + + var alice = new Person('Alice', 'female'), + bob = new Person('Bob', 'male'), + charlie = new Person('Charlie', 'male'), + harry = new Person('Harry Potter', 'male'); + + function initScope($scope) { + $scope.recipients = [alice, bob, charlie]; + $scope.sender = harry; + } + + beforeEach(module('ngMessageFormat')); + + beforeEach(function() { + inject(['$$messageFormat', '$parse', '$locale', '$interpolate', '$rootScope', function( + messageFormat, parse, locale, interpolate, rootScope) { + $$messageFormat = messageFormat; + $parse = parse; + $interpolate = interpolate; + $locale = locale; + $rootScope = rootScope; + initScope(rootScope); + }]); + }); + + describe('mustache', function() { + function assertMustache(text, expected) { + var parsedFn = $interpolate(text); + expect(parsedFn($rootScope)).toEqual(expected); + } + + it('should suppress falsy objects', function() { + assertMustache('{{undefined}}', ''); + assertMustache('{{null}}', ''); + assertMustache('{{a.b}}', ''); + }); + + it('should jsonify objects', function() { + assertMustache('{{ {} }}', '{}'); + assertMustache('{{ true }}', 'true'); + assertMustache('{{ false }}', 'false'); + assertMustache('{{ 1 }}', '1'); + assertMustache('{{ \'1\' }}', '1'); + assertMustache('{{ sender }}', '{"name":"Harry Potter","gender":"male"}'); + }); + + it('should return function that can be called with no context', inject(function($interpolate) { + expect($interpolate('{{sender.name}}')()).toEqual(''); + })); + + describe('watchable', function() { + it('ckck', function() { + var calls = []; + $rootScope.$watch($interpolate('{{::name}}'), function(val) { + calls.push(val); + }); + + $rootScope.$apply(); + expect(calls.length).toBe(1); + + $rootScope.name = 'foo'; + $rootScope.$apply(); + expect(calls.length).toBe(2); + expect(calls[1]).toBe('foo'); + + $rootScope.name = 'bar'; + $rootScope.$apply(); + expect(calls.length).toBe(2); + }); + + + it('should stop watching strings with no expressions after first execution', function() { + var spy = jasmine.createSpy(); + $rootScope.$watch($$messageFormat.interpolate('foo'), spy); + $rootScope.$digest(); + expect($rootScope.$countWatchers()).toBe(0); + expect(spy).toHaveBeenCalledWith('foo', 'foo', $rootScope); + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should stop watching strings with only constant expressions after first execution', function() { + var spy = jasmine.createSpy(); + $rootScope.$watch($$messageFormat.interpolate('foo {{42}}'), spy); + $rootScope.$digest(); + expect($rootScope.$countWatchers()).toBe(0); + expect(spy).toHaveBeenCalledWith('foo 42', 'foo 42', $rootScope); + expect(spy).toHaveBeenCalledTimes(1); + }); + + + }); + + describe('plural', function() { + it('no interpolation', function() { + var text = '' + + '{{recipients.length, plural,\n' + + ' =0 {You gave no gifts}\n' + + ' =1 {You gave one person a gift}\n' + + // "=1" should override "one" for exact value. + ' one {YOU SHOULD NEVER SEE THIS MESSAGE}\n' + + ' other {You gave some people gifts}\n' + + '}}'; + var parsedFn = $interpolate(text, /*mustHaveExpression=*/true); + expect(parsedFn.expressions.length).toBe(1); + expect(parsedFn.expressions[0]).toEqual('recipients.length'); + + $rootScope.recipients.length = 2; + expect(parsedFn($rootScope)).toEqual('You gave some people gifts'); + + $rootScope.recipients.length = 1; + expect(parsedFn($rootScope)).toEqual('You gave one person a gift'); + + $rootScope.recipients.length = 0; + expect(parsedFn($rootScope)).toEqual('You gave no gifts'); + }); + + it('with interpolation', function() { + var text = '' + + '{{recipients.length, plural,\n' + + ' =0 {{{sender.name}} gave no gifts}\n' + + ' =1 {{{sender.name}} gave one gift to {{recipients[0].name}}}\n' + + // "=1" should override "one" for exact value. + ' one {YOU SHOULD NEVER SEE THIS MESSAGE}\n' + + ' other {{{sender.name}} gave them a gift}\n' + + '}}'; + var parsedFn = $interpolate(text, /*mustHaveExpression=*/true); + expect(parsedFn.expressions.length).toBe(1); + expect(parsedFn.expressions[0]).toEqual('recipients.length'); + + $rootScope.recipients.length = 2; + expect(parsedFn($rootScope)).toEqual('Harry Potter gave them a gift'); + + $rootScope.recipients.length = 1; + expect(parsedFn($rootScope)).toEqual('Harry Potter gave one gift to Alice'); + + $rootScope.recipients.length = 0; + expect(parsedFn($rootScope)).toEqual('Harry Potter gave no gifts'); + }); + + it('with offset, interpolation, "#" symbol with and without escaping', function() { + var text = '' + + '{{recipients.length, plural, offset:1\n' + + // NOTE: It's nonsensical to use "#" for "=0" with a positive offset. + ' =0 {{{sender.name}} gave no gifts (\\#=#)}\n' + + ' =1 {{{sender.name}} gave one gift to {{recipients[0].name}} (\\#=#)}\n' + + ' one {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\\#=#)}\n' + + ' other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\\#=#)}\n' + + '}}'; + var parsedFn = $interpolate(text, /*mustHaveExpression=*/true); + expect(parsedFn.expressions.length).toBe(1); + expect(parsedFn.expressions[0]).toEqual('recipients.length'); + + $rootScope.recipients.length = 3; + // "#" should get replaced with the value of "recipients.length - offset" + expect(parsedFn($rootScope)).toEqual('Harry Potter gave Alice and 2 other people a gift (#=2)'); + + $rootScope.recipients.length = 2; + expect(parsedFn($rootScope)).toEqual('Harry Potter gave Alice and one other person a gift (#=1)'); + + $rootScope.recipients.length = 1; + expect(parsedFn($rootScope)).toEqual('Harry Potter gave one gift to Alice (#=0)'); + + $rootScope.recipients.length = 0; + expect(parsedFn($rootScope)).toEqual('Harry Potter gave no gifts (#=-1)'); + }); + }); + + it('nested plural and select', function() { + var text = '' + + '{{recipients.length, plural,\n' + + ' =0 {You gave no gifts}\n' + + ' =1 {{{recipients[0].gender, select,\n' + + ' male {You gave him a gift. -{{sender.name}}}\n' + + ' female {You gave her a gift. -{{sender.name}}}\n' + + ' other {You gave them a gift. -{{sender.name}}}\n' + + ' }}\n' + + ' }\n' + + ' other {You gave {{recipients.length}} people gifts. -{{sender.name}}}\n' + + '}}'; + var parsedFn = $interpolate(text, /*mustHaveExpression=*/true); + expect(parsedFn.expressions.length).toBe(1); + expect(parsedFn.expressions[0]).toEqual('recipients.length'); + var result = parsedFn($rootScope); + expect(result).toEqual('You gave 3 people gifts. -Harry Potter'); + }); + }); + + describe('interpolate', function() { + function assertInterpolation(text, expected) { + var parsedFn = $$messageFormat.interpolate(text); + expect(parsedFn($rootScope)).toEqual(expected); + } + + it('should interpolate a plain string', function() { + assertInterpolation(' Hello, world! ', ' Hello, world! '); + }); + + it('should interpolate a simple expression', function() { + assertInterpolation('Hello, {{sender.name}}!', 'Hello, Harry Potter!'); + }); + }); + }); + + + /* NOTE: This describe block includes a copy of interpolateSpec.js to test that + * $$messageFormat.interpolate behaves the same as $interpolate. + * ONLY the following changes have been made. + * - Add beforeEach(module('ngMessageFormat')) at top level of describe() + * - Add extra "}" for it('should not unescape markers within expressions'). Original + * $interpolate has a bug/feature where a "}}" inside a string is also treated as a + * closing symbol. The new service understands the string context and fixes this. + * - All tests for startSymbol/endSymbol have been commented out. The new service does not + * allow you to change them as of now. + * - Instead, I've added tests to assert that we throw an exception if used with redefined + * startSymbol/endSymbol. These tests are listed right in the beginning before the + * others. allow you to change them as of now. + */ + describe('$interpolate', function() { + beforeEach(module('ngMessageFormat')); + + describe('startSymbol', function() { + it('should expose the startSymbol in run phase', inject(function($interpolate) { + expect($interpolate.startSymbol()).toBe('{{'); + })); + describe('redefinition', function() { + beforeEach(module(function($interpolateProvider) { + expect($interpolateProvider.startSymbol()).toBe('{{'); + $interpolateProvider.startSymbol('(('); + })); + it('should not work when the startSymbol is redefined', function() { + expect(function() { + inject(inject(function($interpolate) {})); + }).toThrowMinErr('$interpolate', 'nochgmustache'); + }); + }); + }); + + describe('endSymbol', function() { + it('should expose the endSymbol in run phase', inject(function($interpolate) { + expect($interpolate.endSymbol()).toBe('}}'); + })); + describe('redefinition', function() { + beforeEach(module(function($interpolateProvider) { + expect($interpolateProvider.endSymbol()).toBe('}}'); + $interpolateProvider.endSymbol('))'); + })); + it('should not work when the endSymbol is redefined', function() { + expect(function() { + inject(inject(function($interpolate) {})); + }).toThrowMinErr('$interpolate', 'nochgmustache'); + }); + }); + }); + + it('should return the interpolation object when there are no bindings and textOnly is undefined', + inject(function($interpolate) { + var interpolateFn = $interpolate('some text'); + + expect(interpolateFn.exp).toBe('some text'); + expect(interpolateFn.expressions).toEqual([]); + + expect(interpolateFn({})).toBe('some text'); + })); + + + it('should return undefined when there are no bindings and textOnly is set to true', + inject(function($interpolate) { + expect($interpolate('some text', true)).toBeUndefined(); + })); + + it('should return undefined when there are bindings and strict is set to true', + inject(function($interpolate) { + expect($interpolate('test {{foo}}', false, null, true)({})).toBeUndefined(); + })); + + it('should suppress falsy objects', inject(function($interpolate) { + expect($interpolate('{{undefined}}')({})).toEqual(''); + expect($interpolate('{{null}}')({})).toEqual(''); + expect($interpolate('{{a.b}}')({})).toEqual(''); + })); + + it('should jsonify objects', inject(function($interpolate) { + expect($interpolate('{{ {} }}')({})).toEqual('{}'); + expect($interpolate('{{ true }}')({})).toEqual('true'); + expect($interpolate('{{ false }}')({})).toEqual('false'); + })); + + + it('should use custom toString when present', inject(function($interpolate, $rootScope) { + var context = { + a: { + toString: function() { + return 'foo'; + } + } + }; + + expect($interpolate('{{ a }}')(context)).toEqual('foo'); + })); + + it('should NOT use toString on array objects', inject(function($interpolate) { + expect($interpolate('{{a}}')({ a: [] })).toEqual('[]'); + })); + + + it('should NOT use toString on Date objects', inject(function($interpolate) { + var date = new Date(2014, 10, 10); + expect($interpolate('{{a}}')({ a: date })).toBe(JSON.stringify(date)); + expect($interpolate('{{a}}')({ a: date })).not.toEqual(date.toString()); + })); + + + it('should return interpolation function', inject(function($interpolate, $rootScope) { + var interpolateFn = $interpolate('Hello {{name}}!'); + + expect(interpolateFn.exp).toBe('Hello {{name}}!'); + expect(interpolateFn.expressions).toEqual(['name']); + + var scope = $rootScope.$new(); + scope.name = 'Bubu'; + + expect(interpolateFn(scope)).toBe('Hello Bubu!'); + })); + + + it('should ignore undefined model', inject(function($interpolate) { + expect($interpolate('Hello {{\'World\'}}{{foo}}')({})).toBe('Hello World'); + })); + + + it('should interpolate with undefined context', inject(function($interpolate) { + expect($interpolate('Hello, world!{{bloop}}')()).toBe('Hello, world!'); + })); + + describe('watching', function() { + it('should be watchable with any input types', inject(function($interpolate, $rootScope) { + var lastVal; + $rootScope.$watch($interpolate('{{i}}'), function(val) { + lastVal = val; + }); + $rootScope.$apply(); + expect(lastVal).toBe(''); + + $rootScope.i = null; + $rootScope.$apply(); + expect(lastVal).toBe(''); + + $rootScope.i = ''; + $rootScope.$apply(); + expect(lastVal).toBe(''); + + $rootScope.i = 0; + $rootScope.$apply(); + expect(lastVal).toBe('0'); + + $rootScope.i = [0]; + $rootScope.$apply(); + expect(lastVal).toBe('[0]'); + + $rootScope.i = {a: 1, b: 2}; + $rootScope.$apply(); + expect(lastVal).toBe('{"a":1,"b":2}'); + })); + + it('should be watchable with literal values', inject(function($interpolate, $rootScope) { + var lastVal; + $rootScope.$watch($interpolate('{{1}}{{"2"}}{{true}}{{[false]}}{{ {a: 2} }}'), function(val) { + lastVal = val; + }); + $rootScope.$apply(); + expect(lastVal).toBe('12true[false]{"a":2}'); + + expect($rootScope.$countWatchers()).toBe(0); + })); + + it('should respect one-time bindings for each individual expression', inject(function($interpolate, $rootScope) { + var calls = []; + $rootScope.$watch($interpolate('{{::a | limitTo:1}} {{::s}} {{::i | number}}'), function(val) { + calls.push(val); + }); + + $rootScope.$apply(); + expect(calls.length).toBe(1); + + $rootScope.a = [1]; + $rootScope.$apply(); + expect(calls.length).toBe(2); + expect(calls[1]).toBe('[1] '); + + $rootScope.a = [0]; + $rootScope.$apply(); + expect(calls.length).toBe(2); + + $rootScope.i = $rootScope.a = 123; + $rootScope.s = 'str!'; + $rootScope.$apply(); + expect(calls.length).toBe(3); + expect(calls[2]).toBe('[1] str! 123'); + + expect($rootScope.$countWatchers()).toBe(0); + })); + + it('should stop watching strings with no expressions after first execution', + inject(function($interpolate, $rootScope) { + var spy = jasmine.createSpy(); + $rootScope.$watch($interpolate('foo'), spy); + $rootScope.$digest(); + expect($rootScope.$countWatchers()).toBe(0); + expect(spy).toHaveBeenCalledWith('foo', 'foo', $rootScope); + expect(spy).toHaveBeenCalledTimes(1); + }) + ); + + it('should stop watching strings with only constant expressions after first execution', + inject(function($interpolate, $rootScope) { + var spy = jasmine.createSpy(); + $rootScope.$watch($interpolate('foo {{42}}'), spy); + $rootScope.$digest(); + expect($rootScope.$countWatchers()).toBe(0); + expect(spy).toHaveBeenCalledWith('foo 42', 'foo 42', $rootScope); + expect(spy).toHaveBeenCalledTimes(1); + }) + ); + }); + + describe('interpolation escaping', function() { + var obj; + beforeEach(function() { + obj = {foo: 'Hello', bar: 'World'}; + }); + + + it('should support escaping interpolation signs', inject(function($interpolate) { + expect($interpolate('{{foo}} \\{\\{bar\\}\\}')(obj)).toBe('Hello {{bar}}'); + expect($interpolate('\\{\\{foo\\}\\} {{bar}}')(obj)).toBe('{{foo}} World'); + })); + + + it('should unescape multiple expressions', inject(function($interpolate) { + expect($interpolate('\\{\\{foo\\}\\}\\{\\{bar\\}\\} {{foo}}')(obj)).toBe('{{foo}}{{bar}} Hello'); + expect($interpolate('{{foo}}\\{\\{foo\\}\\}\\{\\{bar\\}\\}')(obj)).toBe('Hello{{foo}}{{bar}}'); + expect($interpolate('\\{\\{foo\\}\\}{{foo}}\\{\\{bar\\}\\}')(obj)).toBe('{{foo}}Hello{{bar}}'); + expect($interpolate('{{foo}}\\{\\{foo\\}\\}{{bar}}\\{\\{bar\\}\\}{{foo}}')(obj)).toBe('Hello{{foo}}World{{bar}}Hello'); + })); + + + /* + *it('should support escaping custom interpolation start/end symbols', function() { + * module(function($interpolateProvider) { + * $interpolateProvider.startSymbol('[['); + * $interpolateProvider.endSymbol(']]'); + * }); + * inject(function($interpolate) { + * expect($interpolate('[[foo]] \\[\\[bar\\]\\]')(obj)).toBe('Hello [[bar]]'); + * }); + *}); + */ + + + it('should unescape incomplete escaped expressions', inject(function($interpolate) { + expect($interpolate('\\{\\{foo{{foo}}')(obj)).toBe('{{fooHello'); + expect($interpolate('\\}\\}foo{{foo}}')(obj)).toBe('}}fooHello'); + expect($interpolate('foo{{foo}}\\{\\{')(obj)).toBe('fooHello{{'); + expect($interpolate('foo{{foo}}\\}\\}')(obj)).toBe('fooHello}}'); + })); + + + it('should not unescape markers within expressions', inject(function($interpolate) { + expect($interpolate('{{"\\\\{\\\\{Hello, world!\\\\}\\\\}"}}')(obj)).toBe('\\{\\{Hello, world!\\}\\}'); + expect($interpolate('{{"\\{\\{Hello, world!\\}\\}"}}')(obj)).toBe('{{Hello, world!}}'); + expect(function() { + $interpolate('{{\\{\\{foo\\}\\}}}')(obj); + }).toThrowMinErr('$parse', 'lexerr', + 'Lexer Error: Unexpected next character at columns 0-0 [\\] in expression [\\{\\{foo\\}\\}]'); + })); + + + // This test demonstrates that the web-server is responsible for escaping every single instance + // of interpolation start/end markers in an expression which they do not wish to evaluate, + // because AngularJS will not protect them from being evaluated (due to the added complexity + // and maintenance burden of context-sensitive escaping) + it('should evaluate expressions between escaped start/end symbols', inject(function($interpolate) { + expect($interpolate('\\{\\{Hello, {{bar}}!\\}\\}')(obj)).toBe('{{Hello, World!}}'); + })); + }); + + + describe('interpolating in a trusted context', function() { + var sce; + beforeEach(function() { + function log() {} + var fakeLog = {log: log, warn: log, info: log, error: log}; + module(function($provide, $sceProvider) { + $provide.value('$log', fakeLog); + $sceProvider.enabled(true); + }); + inject(['$sce', function($sce) { sce = $sce; }]); + }); + + it('should NOT interpolate non-trusted expressions', inject(function($interpolate, $rootScope) { + var scope = $rootScope.$new(); + scope.foo = 'foo'; + + expect(function() { + $interpolate('{{foo}}', true, sce.CSS)(scope); + }).toThrowMinErr('$interpolate', 'interr'); + })); + + it('should NOT interpolate mistyped expressions', inject(function($interpolate, $rootScope) { + var scope = $rootScope.$new(); + scope.foo = sce.trustAsCss('foo'); + + expect(function() { + $interpolate('{{foo}}', true, sce.HTML)(scope); + }).toThrowMinErr('$interpolate', 'interr'); + })); + + it('should interpolate trusted expressions in a regular context', inject(function($interpolate) { + var foo = sce.trustAsCss('foo'); + expect($interpolate('{{foo}}', true)({foo: foo})).toBe('foo'); + })); + + it('should interpolate trusted expressions in a specific trustedContext', inject(function($interpolate) { + var foo = sce.trustAsCss('foo'); + expect($interpolate('{{foo}}', true, sce.CSS)({foo: foo})).toBe('foo'); + })); + + // The concatenation of trusted values does not necessarily result in a trusted value. (For + // instance, you can construct evil JS code by putting together pieces of JS strings that are by + // themselves safe to execute in isolation.) + it('should NOT interpolate trusted expressions with multiple parts', inject(function($interpolate) { + var foo = sce.trustAsCss('foo'); + var bar = sce.trustAsCss('bar'); + expect(function() { + return $interpolate('{{foo}}{{bar}}', true, sce.CSS)({foo: foo, bar: bar}); + }).toThrowMinErr( + '$interpolate', 'noconcat', 'Error while interpolating: {{foo}}{{bar}}\n' + + 'Strict Contextual Escaping disallows interpolations that concatenate multiple ' + + 'expressions when a trusted value is required. See ' + + 'http://docs.angularjs.org/api/ng.$sce'); + })); + }); + + +/* + * describe('provider', function() { + * beforeEach(module(function($interpolateProvider) { + * $interpolateProvider.startSymbol('--'); + * $interpolateProvider.endSymbol('--'); + * })); + * + * it('should not get confused with same markers', inject(function($interpolate) { + * expect($interpolate('---').expressions).toEqual([]); + * expect($interpolate('----')({})).toEqual(''); + * expect($interpolate('--1--')({})).toEqual('1'); + * })); + * }); + */ + + describe('parseBindings', function() { + it('should Parse Text With No Bindings', inject(function($interpolate) { + expect($interpolate('a').expressions).toEqual([]); + })); + + it('should Parse Empty Text', inject(function($interpolate) { + expect($interpolate('').expressions).toEqual([]); + })); + + it('should Parse Inner Binding', inject(function($interpolate) { + var interpolateFn = $interpolate('a{{b}}C'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['b']); + expect(interpolateFn({b: 123})).toEqual('a123C'); + })); + + it('should Parse Ending Binding', inject(function($interpolate) { + var interpolateFn = $interpolate('a{{b}}'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['b']); + expect(interpolateFn({b: 123})).toEqual('a123'); + })); + + it('should Parse Begging Binding', inject(function($interpolate) { + var interpolateFn = $interpolate('{{b}}c'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['b']); + expect(interpolateFn({b: 123})).toEqual('123c'); + })); + + it('should Parse Loan Binding', inject(function($interpolate) { + var interpolateFn = $interpolate('{{b}}'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['b']); + expect(interpolateFn({b: 123})).toEqual('123'); + })); + + it('should Parse Two Bindings', inject(function($interpolate) { + var interpolateFn = $interpolate('{{b}}{{c}}'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['b', 'c']); + expect(interpolateFn({b: 111, c: 222})).toEqual('111222'); + })); + + it('should Parse Two Bindings With Text In Middle', inject(function($interpolate) { + var interpolateFn = $interpolate('{{b}}x{{c}}'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['b', 'c']); + expect(interpolateFn({b: 111, c: 222})).toEqual('111x222'); + })); + + it('should Parse Multiline', inject(function($interpolate) { + var interpolateFn = $interpolate('"X\nY{{A\n+B}}C\nD"'), + expressions = interpolateFn.expressions; + expect(expressions).toEqual(['A\n+B']); + expect(interpolateFn({'A': 'aa', 'B': 'bb'})).toEqual('"X\nYaabbC\nD"'); + })); + }); + + + describe('isTrustedContext', function() { + it('should NOT interpolate a multi-part expression when isTrustedContext is true', inject(function($interpolate) { + var isTrustedContext = true; + expect(function() { + $interpolate('constant/{{var}}', true, isTrustedContext); + }).toThrowMinErr( + '$interpolate', 'noconcat', 'Error while interpolating: constant/{{var}}\nStrict ' + + 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' + + 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce'); + expect(function() { + $interpolate('{{var}}/constant', true, isTrustedContext); + }).toThrowMinErr( + '$interpolate', 'noconcat', 'Error while interpolating: {{var}}/constant\nStrict ' + + 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' + + 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce'); + expect(function() { + $interpolate('{{foo}}{{bar}}', true, isTrustedContext); + }).toThrowMinErr( + '$interpolate', 'noconcat', 'Error while interpolating: {{foo}}{{bar}}\nStrict ' + + 'Contextual Escaping disallows interpolations that concatenate multiple expressions ' + + 'when a trusted value is required. See http://docs.angularjs.org/api/ng.$sce'); + })); + + it('should interpolate a multi-part expression when isTrustedContext is false', inject(function($interpolate) { + expect($interpolate('some/{{id}}')({})).toEqual('some/'); + expect($interpolate('some/{{id}}')({id: 1})).toEqual('some/1'); + expect($interpolate('{{foo}}{{bar}}')({foo: 1, bar: 2})).toEqual('12'); + })); + }); + +/* + * describe('startSymbol', function() { + * + * beforeEach(module(function($interpolateProvider) { + * expect($interpolateProvider.startSymbol()).toBe('{{'); + * $interpolateProvider.startSymbol('(('); + * })); + * + * + * it('should expose the startSymbol in config phase', module(function($interpolateProvider) { + * expect($interpolateProvider.startSymbol()).toBe('(('); + * })); + * + * + * it('should expose the startSymbol in run phase', inject(function($interpolate) { + * expect($interpolate.startSymbol()).toBe('(('); + * })); + * + * + * it('should not get confused by matching start and end symbols', function() { + * module(function($interpolateProvider) { + * $interpolateProvider.startSymbol('--'); + * $interpolateProvider.endSymbol('--'); + * }); + * + * inject(function($interpolate) { + * expect($interpolate('---').expressions).toEqual([]); + * expect($interpolate('----')({})).toEqual(''); + * expect($interpolate('--1--')({})).toEqual('1'); + * }); + * }); + * }); + */ + + +/* + * describe('endSymbol', function() { + * + * beforeEach(module(function($interpolateProvider) { + * expect($interpolateProvider.endSymbol()).toBe('}}'); + * $interpolateProvider.endSymbol('))'); + * })); + * + * + * it('should expose the endSymbol in config phase', module(function($interpolateProvider) { + * expect($interpolateProvider.endSymbol()).toBe('))'); + * })); + * + * + * it('should expose the endSymbol in run phase', inject(function($interpolate) { + * expect($interpolate.endSymbol()).toBe('))'); + * })); + * }); + */ + + }); // end of tests copied from $interpolate +}); diff --git a/test/ngMessages/messagesSpec.js b/test/ngMessages/messagesSpec.js index a0b39ddf8570..527a577b1f18 100644 --- a/test/ngMessages/messagesSpec.js +++ b/test/ngMessages/messagesSpec.js @@ -4,23 +4,18 @@ describe('ngMessages', function() { beforeEach(inject.strictDi()); beforeEach(module('ngMessages')); - function they(msg, vals, spec, focus) { - forEach(vals, function(val, key) { - var m = msg.replace('$prop', key); - (focus ? iit : it)(m, function() { - spec(val); - }); - }); - } - - function tthey(msg, vals, spec) { - they(msg, vals, spec, true); + function messageChildren(element) { + return (element.length ? element[0] : element).querySelectorAll('[ng-message], [ng-message-exp]'); } function s(str) { return str.replace(/\s+/g,''); } + function trim(value) { + return isString(value) ? value.trim() : value; + } + var element; afterEach(function() { dealoc(element); @@ -41,7 +36,40 @@ describe('ngMessages', function() { expect(element.text()).toContain('Message is set'); })); - it('should use the data attribute when an element directive is used', + it('should render the same message if multiple message keys match', inject(function($rootScope, $compile) { + element = $compile('
          ' + + '
          Message is set
          ' + + '
          ')($rootScope); + $rootScope.$digest(); + + expect(element.text()).not.toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { one: true }; + }); + + expect(element.text()).toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { two: true, one: false }; + }); + + expect(element.text()).toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { three: true, two: false }; + }); + + expect(element.text()).toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { three: false }; + }); + + expect(element.text()).not.toContain('Message is set'); + })); + + it('should use the when attribute when an element directive is used', inject(function($rootScope, $compile) { element = $compile('' + @@ -58,6 +86,111 @@ describe('ngMessages', function() { expect(element.text()).toContain('Message is set'); })); + it('should render the same message if multiple message keys match based on the when attribute', inject(function($rootScope, $compile) { + element = $compile('' + + ' Message is set
          ' + + '')($rootScope); + $rootScope.$digest(); + + expect(element.text()).not.toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { one: true }; + }); + + expect(element.text()).toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { two: true, one: false }; + }); + + expect(element.text()).toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { three: true, two: false }; + }); + + expect(element.text()).toContain('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { three: false }; + }); + + expect(element.text()).not.toContain('Message is set'); + })); + + it('should allow a dynamic expression to be set when ng-message-exp is used', + inject(function($rootScope, $compile) { + + element = $compile('
          ' + + '
          Message is crazy
          ' + + '
          ')($rootScope); + $rootScope.$digest(); + + expect(element.text()).not.toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.variable = 'error'; + $rootScope.col = { error: true }; + }); + + expect(element.text()).toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.col = { error: false, failure: true }; + }); + + expect(element.text()).not.toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.variable = ['failure']; + }); + + expect(element.text()).toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.variable = null; + }); + + expect(element.text()).not.toContain('Message is crazy'); + })); + + it('should allow a dynamic expression to be set when the when-exp attribute is used', + inject(function($rootScope, $compile) { + + element = $compile('' + + ' Message is crazy' + + '')($rootScope); + $rootScope.$digest(); + + expect(element.text()).not.toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.variable = 'error, failure'; + $rootScope.col = { error: true }; + }); + + expect(element.text()).toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.col = { error: false, failure: true }; + }); + + expect(element.text()).toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.variable = []; + }); + + expect(element.text()).not.toContain('Message is crazy'); + + $rootScope.$apply(function() { + $rootScope.variable = null; + }); + + expect(element.text()).not.toContain('Message is crazy'); + })); + they('should render empty when $prop is used as a collection value', { 'null': null, 'false': false, @@ -98,7 +231,7 @@ describe('ngMessages', function() { $rootScope.col = {}; }); - expect(element.children().length).toBe(0); + expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(''); $rootScope.$apply(function() { @@ -108,7 +241,7 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); + expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('This message is blue'); $rootScope.$apply(function() { @@ -117,13 +250,13 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); + expect(messageChildren(element).length).toBe(1); expect(trim(element.text())).toEqual('This message is red'); $rootScope.$apply(function() { $rootScope.col = null; }); - expect(element.children().length).toBe(0); + expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(''); @@ -134,7 +267,7 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(0); + expect(messageChildren(element).length).toBe(0); expect(trim(element.text())).toEqual(''); }); }); @@ -189,6 +322,145 @@ describe('ngMessages', function() { expect(element.hasClass('ng-inactive')).toBe(false); })); + it('should automatically re-render the messages when other directives dynamically change them', + inject(function($rootScope, $compile) { + + element = $compile('
          ' + + '
          Enter something
          ' + + '
          ' + + '
          {{ item.text }}
          ' + + '
          ' + + '
          ')($rootScope); + + $rootScope.$apply(function() { + $rootScope.col = {}; + $rootScope.items = [ + { text: 'Your age is incorrect', name: 'age' }, + { text: 'You\'re too tall man!', name: 'height' }, + { text: 'Your hair is too long', name: 'hair' } + ]; + }); + + expect(messageChildren(element).length).toBe(0); + expect(trim(element.text())).toEqual(''); + + $rootScope.$apply(function() { + $rootScope.col = { hair: true }; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Your hair is too long'); + + $rootScope.$apply(function() { + $rootScope.col = { age: true, hair: true}; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Your age is incorrect'); + + $rootScope.$apply(function() { + // remove the age! + $rootScope.items.shift(); + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Your hair is too long'); + + $rootScope.$apply(function() { + // remove the hair! + $rootScope.items.length = 0; + $rootScope.col.primary = true; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Enter something'); + })); + + + it('should be compatible with ngBind', + inject(function($rootScope, $compile) { + + element = $compile('
          ' + + '
          ' + + '
          ' + + '
          ')($rootScope); + + $rootScope.$apply(function() { + $rootScope.col = { + required: true, + extra: true + }; + $rootScope.errorMessages = { + required: 'Fill in the text field.', + extra: 'Extra error message.' + }; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Fill in the text field.'); + + $rootScope.$apply(function() { + $rootScope.col.required = false; + $rootScope.col.extra = true; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Extra error message.'); + + $rootScope.$apply(function() { + $rootScope.errorMessages.extra = 'New error message.'; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('New error message.'); + })); + + + // issue #12856 + it('should only detach the message object that is associated with the message node being removed', + inject(function($rootScope, $compile, $animate) { + + // We are going to spy on the `leave` method to give us control over + // when the element is actually removed + spyOn($animate, 'leave'); + + // Create a basic ng-messages set up + element = $compile('
          ' + + '
          Enter something
          ' + + '
          ')($rootScope); + + // Trigger the message to be displayed + $rootScope.col = { primary: true }; + $rootScope.$digest(); + expect(messageChildren(element).length).toEqual(1); + var oldMessageNode = messageChildren(element)[0]; + + // Remove the message + $rootScope.col = { primary: undefined }; + $rootScope.$digest(); + + // Since we have spied on the `leave` method, the message node is still in the DOM + expect($animate.leave).toHaveBeenCalledOnce(); + var nodeToRemove = $animate.leave.calls.mostRecent().args[0][0]; + expect(nodeToRemove).toBe(oldMessageNode); + $animate.leave.calls.reset(); + + // Add the message back in + $rootScope.col = { primary: true }; + $rootScope.$digest(); + + // Simulate the animation completing on the node + jqLite(nodeToRemove).remove(); + + // We should not get another call to `leave` + expect($animate.leave).not.toHaveBeenCalled(); + + // There should only be the new message node + expect(messageChildren(element).length).toEqual(1); + var newMessageNode = messageChildren(element)[0]; + expect(newMessageNode).not.toBe(oldMessageNode); + })); + it('should render animations when the active/inactive classes are added/removed', function() { module('ngAnimate'); module('ngAnimateMock'); @@ -217,12 +489,362 @@ describe('ngMessages', function() { }); }); + describe('ngMessage nested nested inside elements', function() { + + it('should not crash or leak memory when the messages are transcluded, the first message is ' + + 'visible, and ngMessages is removed by ngIf', function() { + + module(function($compileProvider) { + $compileProvider.directive('messageWrap', function() { + return { + transclude: true, + scope: { + col: '=col' + }, + template: '
          ' + }; + }); + }); + + inject(function($rootScope, $compile) { + + element = $compile('
          ' + + '
          A
          ' + + '
          B
          ' + + '
          ')($rootScope); + + $rootScope.$apply(function() { + $rootScope.show = true; + $rootScope.col = { + a: true, + b: true + }; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('A'); + + $rootScope.$apply('show = false'); + + expect(messageChildren(element).length).toBe(0); + }); + }); + + + it('should not crash when the first of two nested messages is removed', function() { + inject(function($rootScope, $compile) { + + element = $compile( + '
          ' + + '
          ' + + '
          A
          ' + + '
          B
          ' + + '
          ' + + '
          ' + )($rootScope); + + $rootScope.$apply(function() { + $rootScope.col = { + a: true, + b: false + }; + }); + + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('A'); + + var ctrl = element.controller('ngMessages'); + var deregisterSpy = spyOn(ctrl, 'deregister').and.callThrough(); + + var nodeA = element[0].querySelector('[ng-message="a"]'); + jqLite(nodeA).remove(); + $rootScope.$digest(); // The next digest triggers the error + + // Make sure removing the element triggers the deregistration in ngMessages + expect(trim(deregisterSpy.calls.mostRecent().args[0].nodeValue)).toBe('ngMessage: a'); + expect(messageChildren(element).length).toBe(0); + }); + }); + + + it('should not crash, but show deeply nested messages correctly after a message ' + + 'has been removed', function() { + inject(function($rootScope, $compile) { + + element = $compile( + '
          ' + + '
          ' + + '
          A
          ' + + '
          ' + + '
          B
          ' + + '
          C
          ' + + '
          ' + + '
          D
          ' + + '
          ' + + '
          ' + )($rootScope); + + $rootScope.$apply(function() { + $rootScope.col = { + a: true, + b: true + }; + }); + + expect(messageChildren(element).length).toBe(2); + expect(trim(element.text())).toEqual('AB'); + + var ctrl = element.controller('ngMessages'); + var deregisterSpy = spyOn(ctrl, 'deregister').and.callThrough(); + + var nodeB = element[0].querySelector('[ng-message="b"]'); + jqLite(nodeB).remove(); + $rootScope.$digest(); // The next digest triggers the error + + // Make sure removing the element triggers the deregistration in ngMessages + expect(trim(deregisterSpy.calls.mostRecent().args[0].nodeValue)).toBe('ngMessage: b'); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('A'); + }); + }); + }); + + + it('should clean-up the ngMessage scope when a message is removed', + inject(function($compile, $rootScope) { + + var html = + '
          ' + + '
          {{forA}}
          ' + + '
          '; + + element = $compile(html)($rootScope); + $rootScope.$apply(function() { + $rootScope.forA = 'A'; + $rootScope.items = {a: true}; + }); + + expect(element.text()).toBe('A'); + var watchers = $rootScope.$countWatchers(); + + $rootScope.$apply('items.a = false'); + + expect(element.text()).toBe(''); + // We don't know exactly how many watchers are on the scope, only that there should be + // one less now + expect($rootScope.$countWatchers()).toBe(watchers - 1); + }) + ); + + it('should unregister the ngMessage even if it was never attached', + inject(function($compile, $rootScope) { + var html = + '
          ' + + '
          ERROR
          ' + + '
          '; + + element = $compile(html)($rootScope); + + var ctrl = element.controller('ngMessages'); + + expect(messageChildren(element).length).toBe(0); + expect(Object.keys(ctrl.messages).length).toEqual(0); + + $rootScope.$apply('show = true'); + expect(messageChildren(element).length).toBe(0); + expect(Object.keys(ctrl.messages).length).toEqual(1); + + $rootScope.$apply('show = false'); + expect(messageChildren(element).length).toBe(0); + expect(Object.keys(ctrl.messages).length).toEqual(0); + }) + ); + + + describe('default message', function() { + it('should render a default message when no message matches', inject(function($rootScope, $compile) { + element = $compile('
          ' + + '
          Message is set
          ' + + '
          Default message is set
          ' + + '
          ')($rootScope); + $rootScope.$apply(function() { + $rootScope.col = { unexpected: false }; + }); + + $rootScope.$digest(); + + expect(element.text().trim()).toBe(''); + expect(element).not.toHaveClass('ng-active'); + + $rootScope.$apply(function() { + $rootScope.col = { unexpected: true }; + }); + + expect(element.text().trim()).toBe('Default message is set'); + expect(element).toHaveClass('ng-active'); + + $rootScope.$apply(function() { + $rootScope.col = { unexpected: false }; + }); + + expect(element.text().trim()).toBe(''); + expect(element).not.toHaveClass('ng-active'); + + $rootScope.$apply(function() { + $rootScope.col = { val: true, unexpected: true }; + }); + + expect(element.text().trim()).toBe('Message is set'); + expect(element).toHaveClass('ng-active'); + })); + + it('should not render a default message with ng-messages-multiple if another error matches', + inject(function($rootScope, $compile) { + element = $compile('
          ' + + '
          Message is set
          ' + + '
          Other message is set
          ' + + '
          Default message is set
          ' + + '
          ')($rootScope); + + expect(element.text().trim()).toBe(''); + + $rootScope.$apply(function() { + $rootScope.col = { val: true, other: false, unexpected: false }; + }); + + expect(element.text().trim()).toBe('Message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { val: true, other: true, unexpected: true }; + }); + + expect(element.text().trim()).toBe('Message is set Other message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { val: false, other: false, unexpected: true }; + }); + + expect(element.text().trim()).toBe('Default message is set'); + }) + ); + + it('should handle a default message with ngIf', inject(function($rootScope, $compile) { + element = $compile('
          ' + + '
          Message is set
          ' + + '
          Default message is set
          ' + + '
          ')($rootScope); + $rootScope.default = true; + $rootScope.col = {unexpected: true}; + $rootScope.$digest(); + + expect(element.text().trim()).toBe('Default message is set'); + + $rootScope.$apply('default = false'); + + expect(element.text().trim()).toBe(''); + + $rootScope.$apply('default = true'); + + expect(element.text().trim()).toBe('Default message is set'); + + $rootScope.$apply(function() { + $rootScope.col = { val: true }; + }); + + expect(element.text().trim()).toBe('Message is set'); + })); + }); + describe('when including templates', function() { + they('should work with a dynamic collection model which is managed by ngRepeat', + {'
          ': '
          ' + + '
          ' + + '
          ', + '': '' + + '' + + ''}, + function(html) { + inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('abc.html', '
          A
          ' + + '
          B
          ' + + '
          C
          '); + + html = '
          ' + html + '
          '; + $rootScope.items = [{},{},{}]; + + element = $compile(html)($rootScope); + $rootScope.$apply(function() { + $rootScope.items[0].a = true; + $rootScope.items[1].b = true; + $rootScope.items[2].c = true; + }); + + var elements = element[0].querySelectorAll('[ng-repeat]'); + + // all three collections should have at least one error showing up + expect(messageChildren(element).length).toBe(3); + expect(messageChildren(elements[0]).length).toBe(1); + expect(messageChildren(elements[1]).length).toBe(1); + expect(messageChildren(elements[2]).length).toBe(1); + + // this is the standard order of the displayed error messages + expect(element.text().trim()).toBe('ABC'); + + $rootScope.$apply(function() { + $rootScope.items[0].a = false; + $rootScope.items[0].c = true; + + $rootScope.items[1].b = false; + + $rootScope.items[2].c = false; + $rootScope.items[2].a = true; + }); + + // with the 2nd item gone and the values changed + // we should see both 1 and 3 changed + expect(element.text().trim()).toBe('CA'); + + $rootScope.$apply(function() { + // add the value for the 2nd item back + $rootScope.items[1].b = true; + $rootScope.items.reverse(); + }); + + // when reversed we get back to our original value + expect(element.text().trim()).toBe('ABC'); + }); + }); + + they('should remove the $prop element and place a comment anchor node where it used to be', + {'
          ': '
          ' + + '
          ' + + '
          ', + '': '' + + '' + + ''}, + function(html) { + inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('abc.html', '
          '); + + element = $compile(html)($rootScope); + $rootScope.$digest(); + + var includeElement = element[0].querySelector('[ng-messages-include], ng-messages-include'); + expect(includeElement).toBeFalsy(); + + var comment = element[0].childNodes[0]; + expect(comment.nodeType).toBe(8); + expect(comment.nodeValue).toBe(' ngMessagesInclude: abc.html '); + }); + }); + they('should load a remote template using $prop', - {'
          ': - '
          ', - '': - ''}, + {'
          ': '
          ' + + '
          ' + + '
          ', + '': '' + + '' + + ''}, function(html) { inject(function($compile, $rootScope, $templateCache) { $templateCache.put('abc.html', '
          A
          ' + @@ -238,8 +860,8 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("A"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('A'); $rootScope.$apply(function() { $rootScope.data = { @@ -247,19 +869,19 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("C"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('C'); }); }); it('should cache the template after download', inject(function($rootScope, $compile, $templateCache, $httpBackend) { - $httpBackend.expect('GET', 'tpl').respond(201, 'abc'); + $httpBackend.expect('GET', 'tpl').respond(201, '
          abc
          '); expect($templateCache.get('tpl')).toBeUndefined(); - element = $compile('
          ')($rootScope); + element = $compile('
          ')($rootScope); $rootScope.$digest(); $httpBackend.flush(); @@ -270,9 +892,11 @@ describe('ngMessages', function() { it('should re-render the messages after download without an extra digest', inject(function($rootScope, $compile, $httpBackend) { - $httpBackend.expect('GET', 'my-messages').respond(201,'
          You did not enter a value
          '); + $httpBackend.expect('GET', 'my-messages').respond(201, + '
          You did not enter a value
          '); - element = $compile('
          ' + + element = $compile('
          ' + + '
          ' + '
          Your value is that of failure
          ' + '
          ')($rootScope); @@ -283,24 +907,26 @@ describe('ngMessages', function() { $rootScope.$digest(); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("Your value is that of failure"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('Your value is that of failure'); $httpBackend.flush(); + $rootScope.$digest(); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("You did not enter a value"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('You did not enter a value'); })); - it('should allow for overriding the remote template messages within the element', + it('should allow for overriding the remote template messages within the element depending on where the remote template is placed', inject(function($compile, $rootScope, $templateCache) { $templateCache.put('abc.html', '
          A
          ' + '
          B
          ' + '
          C
          '); - element = $compile('
          ' + + element = $compile('
          ' + '
          AAA
          ' + + '
          ' + '
          CCC
          ' + '
          ')($rootScope); @@ -312,8 +938,8 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("AAA"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('AAA'); $rootScope.$apply(function() { $rootScope.data = { @@ -322,8 +948,8 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("B"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('B'); $rootScope.$apply(function() { $rootScope.data = { @@ -331,53 +957,78 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("CCC"); + expect(messageChildren(element).length).toBe(1); + expect(trim(element.text())).toEqual('C'); })); - it('should retain the order of the remote template\'s messages when overriding within the element', + it('should properly detect a previous message, even if it was registered later', inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('include.html', '
          A
          '); + var html = + '
          ' + + '
          ' + + '
          B
          ' + + '
          C
          ' + + '
          '; - $templateCache.put('abc.html', '
          C
          ' + - '
          A
          ' + - '
          B
          '); + element = $compile(html)($rootScope); + $rootScope.$apply('items = {b: true, c: true}'); - element = $compile('
          ' + - '
          AAA
          ' + - '
          CCC
          ' + - '
          ')($rootScope); + expect(element.text()).toBe('B'); - $rootScope.$apply(function() { - $rootScope.data = { - 'a': 1, - 'b': 2, - 'c': 3 - }; - }); + var ctrl = element.controller('ngMessages'); + var deregisterSpy = spyOn(ctrl, 'deregister').and.callThrough(); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("CCC"); + var nodeB = element[0].querySelector('[ng-message="b"]'); + jqLite(nodeB).remove(); - $rootScope.$apply(function() { - $rootScope.data = { - 'a': 1, - 'b': 2 - }; - }); + // Make sure removing the element triggers the deregistration in ngMessages + expect(trim(deregisterSpy.calls.mostRecent().args[0].nodeValue)).toBe('ngMessage: b'); - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("AAA"); + $rootScope.$apply('items.a = true'); - $rootScope.$apply(function() { - $rootScope.data = { - 'b': 3 - }; - }); + expect(element.text()).toBe('A'); + }) + ); + + it('should not throw if scope has been destroyed when template request is ready', + inject(function($rootScope, $httpBackend, $compile) { + $httpBackend.expectGET('messages.html').respond('
          A
          '); + $rootScope.show = true; + var html = + '
          ' + + '
          ' + + '
          ' + + '
          ' + + '
          '; - expect(element.children().length).toBe(1); - expect(trim(element.text())).toEqual("B"); + element = $compile(html)($rootScope); + $rootScope.$digest(); + $rootScope.show = false; + $rootScope.$digest(); + expect(function() { + $httpBackend.flush(); + }).not.toThrow(); })); + it('should not throw if the template is empty', + inject(function($compile, $rootScope, $templateCache) { + var html = + '
          ' + + '
          ' + + '
          ' + + '
          '; + + $templateCache.put('messages1.html', ''); + $templateCache.put('messages2.html', ' '); + element = $compile(html)($rootScope); + $rootScope.$digest(); + + expect(element.text()).toBe(''); + expect(element.children().length).toBe(0); + expect(element.contents().length).toBe(2); + }) + ); }); describe('when multiple', function() { @@ -400,8 +1051,8 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(2); - expect(s(element.text())).toContain("13"); + expect(messageChildren(element).length).toBe(2); + expect(s(element.text())).toContain('13'); }); }); @@ -412,9 +1063,9 @@ describe('ngMessages', function() { '
          Y
          ' + '
          Z
          '); - element = $compile('
          ')($rootScope); + element = $compile('
          ' + + '
          ' + + '
          ')($rootScope); $rootScope.$apply(function() { $rootScope.data = { @@ -424,15 +1075,15 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(2); - expect(s(element.text())).toEqual("XZ"); + expect(messageChildren(element).length).toBe(2); + expect(s(element.text())).toEqual('XZ'); $rootScope.$apply(function() { $rootScope.data.y = {}; }); - expect(element.children().length).toBe(3); - expect(s(element.text())).toEqual("XYZ"); + expect(messageChildren(element).length).toBe(3); + expect(s(element.text())).toEqual('XYZ'); })); it('should render and override all truthy messages from a remote template', @@ -442,11 +1093,10 @@ describe('ngMessages', function() { '
          Y
          ' + '
          Z
          '); - element = $compile('
          ' + - '
          YYY
          ' + - '
          ZZZ
          ' + + element = $compile('
          ' + + '
          YYY
          ' + + '
          ZZZ
          ' + + '
          ' + '
          ')($rootScope); $rootScope.$apply(function() { @@ -457,15 +1107,15 @@ describe('ngMessages', function() { }; }); - expect(element.children().length).toBe(2); - expect(s(element.text())).toEqual("XZZZ"); + expect(messageChildren(element).length).toBe(2); + expect(s(element.text())).toEqual('ZZZX'); $rootScope.$apply(function() { $rootScope.data.y = {}; }); - expect(element.children().length).toBe(3); - expect(s(element.text())).toEqual("XYYYZZZ"); + expect(messageChildren(element).length).toBe(3); + expect(s(element.text())).toEqual('YYYZZZX'); })); }); }); diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js index 54ea6f340d0f..f8777c517a70 100644 --- a/test/ngMock/angular-mocksSpec.js +++ b/test/ngMock/angular-mocksSpec.js @@ -1,7 +1,9 @@ 'use strict'; describe('ngMock', function() { + var noop = angular.noop; + var extend = angular.extend; describe('TzDate', function() { @@ -26,17 +28,19 @@ describe('ngMock', function() { it('should fake getLocalDateString method', function() { - //0 in -3h - var t0 = new angular.mock.TzDate(-3, 0); - expect(t0.toLocaleDateString()).toMatch('1970'); + var millennium = new Date('2000').getTime(); - //0 in +0h - var t1 = new angular.mock.TzDate(0, 0); - expect(t1.toLocaleDateString()).toMatch('1970'); + // millennium in -3h + var t0 = new angular.mock.TzDate(-3, millennium); + expect(t0.toLocaleDateString()).toMatch('2000'); - //0 in +3h - var t2 = new angular.mock.TzDate(3, 0); - expect(t2.toLocaleDateString()).toMatch('1969'); + // millennium in +0h + var t1 = new angular.mock.TzDate(0, millennium); + expect(t1.toLocaleDateString()).toMatch('2000'); + + // millennium in +3h + var t2 = new angular.mock.TzDate(3, millennium); + expect(t2.toLocaleDateString()).toMatch('1999'); }); @@ -65,7 +69,7 @@ describe('ngMock', function() { //0:00 in +3h var t2 = new angular.mock.TzDate(3, jan2); - expect(t2.getHours()).toMatch(21); + expect(t2.getHours()).toMatch('21'); }); @@ -88,11 +92,11 @@ describe('ngMock', function() { //0:15 in +3h var t2 = new angular.mock.TzDate(3, minutes(15)); - expect(t2.getMinutes()).toMatch(15); + expect(t2.getMinutes()).toMatch('15'); //0:15 in +3.25h var t2a = new angular.mock.TzDate(3.25, minutes(15)); - expect(t2a.getMinutes()).toMatch(0); + expect(t2a.getMinutes()).toMatch('0'); }); @@ -107,7 +111,7 @@ describe('ngMock', function() { //0 in +3h var t2 = new angular.mock.TzDate(3, 0); - expect(t2.getSeconds()).toMatch(0); + expect(t2.getSeconds()).toMatch('0'); }); @@ -154,7 +158,7 @@ describe('ngMock', function() { it('should throw error when no third param but toString called', function() { expect(function() { new angular.mock.TzDate(0,0).toString(); }). - toThrow('Method \'toString\' is not implemented in the TzDate mock'); + toThrowError('Method \'toString\' is not implemented in the TzDate mock'); }); }); @@ -170,7 +174,7 @@ describe('ngMock', function() { $log.reset(); })); - it("should skip debugging output if disabled (" + debugEnabled + ")", inject(function($log) { + it('should skip debugging output if disabled (' + debugEnabled + ')', inject(function($log) { $log.log('fake log'); $log.info('fake log'); $log.warn('fake log'); @@ -294,13 +298,15 @@ describe('ngMock', function() { expect(counter).toBe(1); $interval.flush(1000); - expect(counter).toBe(2); + + $interval.flush(2000); + expect(counter).toBe(4); })); it('should call $apply after each task is executed', inject(function($interval, $rootScope) { - var applySpy = spyOn($rootScope, '$apply').andCallThrough(); + var applySpy = spyOn($rootScope, '$apply').and.callThrough(); $interval(noop, 1000); expect(applySpy).not.toHaveBeenCalled(); @@ -308,27 +314,27 @@ describe('ngMock', function() { $interval.flush(1000); expect(applySpy).toHaveBeenCalledOnce(); - applySpy.reset(); + applySpy.calls.reset(); $interval(noop, 1000); $interval(noop, 1000); $interval.flush(1000); - expect(applySpy.callCount).toBe(3); + expect(applySpy).toHaveBeenCalledTimes(3); })); it('should NOT call $apply if invokeApply is set to false', inject(function($interval, $rootScope) { - var applySpy = spyOn($rootScope, '$apply').andCallThrough(); + var digestSpy = spyOn($rootScope, '$digest').and.callThrough(); var counter = 0; $interval(function increment() { counter++; }, 1000, 0, false); - expect(applySpy).not.toHaveBeenCalled(); + expect(digestSpy).not.toHaveBeenCalled(); expect(counter).toBe(0); $interval.flush(2000); - expect(applySpy).not.toHaveBeenCalled(); + expect(digestSpy).not.toHaveBeenCalled(); expect(counter).toBe(2); })); @@ -347,6 +353,75 @@ describe('ngMock', function() { })); + it('should allow you to NOT specify the delay time', inject(function($interval) { + var counterA = 0; + var counterB = 0; + + $interval(function() { counterA++; }); + $interval(function() { counterB++; }, 0); + + $interval.flush(100); + expect(counterA).toBe(100); + expect(counterB).toBe(100); + $interval.flush(100); + expect(counterA).toBe(200); + expect(counterB).toBe(200); + })); + + + it('should run tasks in correct relative order', inject(function($interval) { + var counterA = 0; + var counterB = 0; + $interval(function() { counterA++; }, 0); + $interval(function() { counterB++; }, 1000); + + $interval.flush(1000); + expect(counterA).toBe(1000); + expect(counterB).toBe(1); + $interval.flush(999); + expect(counterA).toBe(1999); + expect(counterB).toBe(1); + $interval.flush(1); + expect(counterA).toBe(2000); + expect(counterB).toBe(2); + })); + + + it('should NOT trigger zero-delay interval when flush has ran before', inject(function($interval) { + var counterA = 0; + var counterB = 0; + + $interval.flush(100); + + $interval(function() { counterA++; }); + $interval(function() { counterB++; }, 0); + + expect(counterA).toBe(0); + expect(counterB).toBe(0); + + $interval.flush(100); + + expect(counterA).toBe(100); + expect(counterB).toBe(100); + })); + + + it('should trigger zero-delay interval only once on flush zero', inject(function($interval) { + var counterA = 0; + var counterB = 0; + + $interval(function() { counterA++; }); + $interval(function() { counterB++; }, 0); + + $interval.flush(0); + expect(counterA).toBe(1); + expect(counterB).toBe(1); + $interval.flush(0); + expect(counterA).toBe(1); + expect(counterB).toBe(1); + })); + + it('should allow you to specify a number of iterations', inject(function($interval) { var counter = 0; $interval(function() {counter++;}, 1000, 2); @@ -424,22 +499,22 @@ describe('ngMock', function() { it('should delegate exception to the $exceptionHandler service', inject( function($interval, $exceptionHandler) { - $interval(function() { throw "Test Error"; }, 1000); + $interval(function() { throw 'Test Error'; }, 1000); expect($exceptionHandler.errors).toEqual([]); $interval.flush(1000); - expect($exceptionHandler.errors).toEqual(["Test Error"]); + expect($exceptionHandler.errors).toEqual(['Test Error']); $interval.flush(1000); - expect($exceptionHandler.errors).toEqual(["Test Error", "Test Error"]); + expect($exceptionHandler.errors).toEqual(['Test Error', 'Test Error']); })); it('should call $apply even if an exception is thrown in callback', inject( function($interval, $rootScope) { - var applySpy = spyOn($rootScope, '$apply').andCallThrough(); + var applySpy = spyOn($rootScope, '$apply').and.callThrough(); - $interval(function() { throw "Test Error"; }, 1000); + $interval(function() { throw new Error('Test Error'); }, 1000); expect(applySpy).not.toHaveBeenCalled(); $interval.flush(1000); @@ -450,7 +525,7 @@ describe('ngMock', function() { it('should still update the interval promise when an exception is thrown', inject(function($interval) { var log = [], - promise = $interval(function() { throw "Some Error"; }, 1000); + promise = $interval(function() { throw new Error('Some Error'); }, 1000); promise.then(function(value) { log.push('promise success: ' + value); }, function(err) { log.push('promise error: ' + err); }, @@ -528,7 +603,7 @@ describe('ngMock', function() { }); - describe('defer', function() { + describe('$browser', function() { var browser, log; beforeEach(inject(function($browser) { browser = $browser; @@ -541,47 +616,292 @@ describe('ngMock', function() { }; } - it('should flush', function() { - browser.defer(logFn('A')); - expect(log).toEqual(''); - browser.defer.flush(); - expect(log).toEqual('A;'); + describe('defer.flush', function() { + it('should flush', function() { + browser.defer(logFn('A')); + browser.defer(logFn('B'), null, 'taskType'); + expect(log).toEqual(''); + + browser.defer.flush(); + expect(log).toEqual('A;B;'); + }); + + it('should flush delayed', function() { + browser.defer(logFn('A')); + browser.defer(logFn('B'), 0, 'taskTypeB'); + browser.defer(logFn('C'), 10, 'taskTypeC'); + browser.defer(logFn('D'), 20); + expect(log).toEqual(''); + expect(browser.defer.now).toEqual(0); + + browser.defer.flush(0); + expect(log).toEqual('A;B;'); + + browser.defer.flush(); + expect(log).toEqual('A;B;C;D;'); + }); + + it('should defer and flush over time', function() { + browser.defer(logFn('A'), 1); + browser.defer(logFn('B'), 2, 'taskType'); + browser.defer(logFn('C'), 3); + + browser.defer.flush(0); + expect(browser.defer.now).toEqual(0); + expect(log).toEqual(''); + + browser.defer.flush(1); + expect(browser.defer.now).toEqual(1); + expect(log).toEqual('A;'); + + browser.defer.flush(2); + expect(browser.defer.now).toEqual(3); + expect(log).toEqual('A;B;C;'); + }); + + it('should throw an exception if there is nothing to be flushed', function() { + expect(function() {browser.defer.flush();}).toThrowError('No deferred tasks to be flushed'); + }); + + it('should not throw an exception when passing a specific delay', function() { + expect(function() {browser.defer.flush(100);}).not.toThrow(); + }); + + describe('tasks scheduled during flushing', function() { + it('should be flushed if they do not exceed the target delay (when no delay specified)', + function() { + browser.defer(function() { + logFn('1')(); + browser.defer(function() { + logFn('3')(); + browser.defer(logFn('4'), 1); + }, 2); + }, 1); + browser.defer(function() { + logFn('2')(); + browser.defer(logFn('6'), 4); + }, 2); + browser.defer(logFn('5'), 5); + + browser.defer.flush(0); + expect(browser.defer.now).toEqual(0); + expect(log).toEqual(''); + + browser.defer.flush(); + expect(browser.defer.now).toEqual(5); + expect(log).toEqual('1;2;3;4;5;'); + } + ); + + it('should be flushed if they do not exceed the specified delay', + function() { + browser.defer(function() { + logFn('1')(); + browser.defer(function() { + logFn('3')(); + browser.defer(logFn('4'), 1); + }, 2); + }, 1); + browser.defer(function() { + logFn('2')(); + browser.defer(logFn('6'), 4); + }, 2); + browser.defer(logFn('5'), 5); + + browser.defer.flush(0); + expect(browser.defer.now).toEqual(0); + expect(log).toEqual(''); + + browser.defer.flush(4); + expect(browser.defer.now).toEqual(4); + expect(log).toEqual('1;2;3;4;'); + + browser.defer.flush(6); + expect(browser.defer.now).toEqual(10); + expect(log).toEqual('1;2;3;4;5;6;'); + } + ); + }); + }); + + describe('defer.cancel', function() { + it('should cancel a pending task', function() { + var taskId1 = browser.defer(logFn('A'), 100, 'fooType'); + var taskId2 = browser.defer(logFn('B'), 200); + + expect(log).toBe(''); + expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).toThrow(); + expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow(); + + browser.defer.cancel(taskId1); + expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).not.toThrow(); + expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow(); + + browser.defer.cancel(taskId2); + expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).not.toThrow(); + expect(function() {browser.defer.verifyNoPendingTasks();}).not.toThrow(); + + browser.defer.flush(1000); + expect(log).toBe(''); + }); + }); + + describe('defer.verifyNoPendingTasks', function() { + it('should throw if there are pending tasks', function() { + expect(browser.defer.verifyNoPendingTasks).not.toThrow(); + + browser.defer(noop); + expect(browser.defer.verifyNoPendingTasks).toThrow(); + }); + + it('should list the pending tasks (in order) in the error message', function() { + browser.defer(noop, 100); + browser.defer(noop, 300, 'fooType'); + browser.defer(noop, 200, 'barType'); + + var expectedError = + 'Deferred tasks to flush (3):\n' + + ' {id: 0, type: $$default$$, time: 100}\n' + + ' {id: 2, type: barType, time: 200}\n' + + ' {id: 1, type: fooType, time: 300}'; + expect(browser.defer.verifyNoPendingTasks).toThrowError(expectedError); + }); + + describe('with specific task type', function() { + it('should throw if there are pending tasks', function() { + browser.defer(noop, 0, 'fooType'); + + expect(function() {browser.defer.verifyNoPendingTasks('barType');}).not.toThrow(); + expect(function() {browser.defer.verifyNoPendingTasks('fooType');}).toThrow(); + expect(function() {browser.defer.verifyNoPendingTasks();}).toThrow(); + }); + + it('should list the pending tasks (in order) in the error message', function() { + browser.defer(noop, 100); + browser.defer(noop, 300, 'fooType'); + browser.defer(noop, 200, 'barType'); + browser.defer(noop, 400, 'fooType'); + + var expectedError = + 'Deferred tasks to flush (2):\n' + + ' {id: 1, type: fooType, time: 300}\n' + + ' {id: 3, type: fooType, time: 400}'; + expect(function() {browser.defer.verifyNoPendingTasks('fooType');}). + toThrowError(expectedError); + }); + }); }); - it('should flush delayed', function() { - browser.defer(logFn('A')); - browser.defer(logFn('B'), 10); - browser.defer(logFn('C'), 20); - expect(log).toEqual(''); + describe('notifyWhenNoOutstandingRequests', function() { + var callback; + beforeEach(function() { + callback = jasmine.createSpy('callback'); + }); + + it('should immediately run the callback if no pending tasks', function() { + browser.notifyWhenNoOutstandingRequests(callback); + expect(callback).toHaveBeenCalled(); + }); + + it('should run the callback as soon as there are no pending tasks', function() { + browser.defer(noop, 100); + browser.defer(noop, 200); + + browser.notifyWhenNoOutstandingRequests(callback); + expect(callback).not.toHaveBeenCalled(); + + browser.defer.flush(100); + expect(callback).not.toHaveBeenCalled(); + + browser.defer.flush(100); + expect(callback).toHaveBeenCalled(); + }); + + it('should not run the callback more than once', function() { + browser.defer(noop, 100); + browser.notifyWhenNoOutstandingRequests(callback); + expect(callback).not.toHaveBeenCalled(); + + browser.defer.flush(100); + expect(callback).toHaveBeenCalledOnce(); + + browser.defer(noop, 200); + browser.defer.flush(100); + expect(callback).toHaveBeenCalledOnce(); + }); + + describe('with specific task type', function() { + it('should immediately run the callback if no pending tasks', function() { + browser.notifyWhenNoOutstandingRequests(callback, 'fooType'); + expect(callback).toHaveBeenCalled(); + }); + + it('should run the callback as soon as there are no pending tasks', function() { + browser.defer(noop, 100, 'fooType'); + browser.defer(noop, 200, 'barType'); + + browser.notifyWhenNoOutstandingRequests(callback, 'fooType'); + expect(callback).not.toHaveBeenCalled(); + + browser.defer.flush(100); + expect(callback).toHaveBeenCalled(); + }); - expect(browser.defer.now).toEqual(0); - browser.defer.flush(0); - expect(log).toEqual('A;'); + it('should not run the callback more than once', function() { + browser.defer(noop, 100, 'fooType'); + browser.defer(noop, 200); - browser.defer.flush(); - expect(log).toEqual('A;B;C;'); + browser.notifyWhenNoOutstandingRequests(callback, 'fooType'); + expect(callback).not.toHaveBeenCalled(); + + browser.defer.flush(100); + expect(callback).toHaveBeenCalledOnce(); + + browser.defer.flush(100); + expect(callback).toHaveBeenCalledOnce(); + + browser.defer(noop, 100, 'fooType'); + browser.defer(noop, 200); + browser.defer.flush(); + expect(callback).toHaveBeenCalledOnce(); + }); + }); }); + }); - it('should defer and flush over time', function() { - browser.defer(logFn('A'), 1); - browser.defer(logFn('B'), 2); - browser.defer(logFn('C'), 3); - browser.defer.flush(0); - expect(browser.defer.now).toEqual(0); - expect(log).toEqual(''); + describe('$flushPendingTasks', function() { + var $flushPendingTasks; + var browserDeferFlushSpy; + + beforeEach(inject(function($browser, _$flushPendingTasks_) { + $flushPendingTasks = _$flushPendingTasks_; + browserDeferFlushSpy = spyOn($browser.defer, 'flush').and.returnValue('flushed'); + })); - browser.defer.flush(1); - expect(browser.defer.now).toEqual(1); - expect(log).toEqual('A;'); + it('should delegate to `$browser.defer.flush()`', function() { + var result = $flushPendingTasks(42); - browser.defer.flush(2); - expect(browser.defer.now).toEqual(3); - expect(log).toEqual('A;B;C;'); + expect(browserDeferFlushSpy).toHaveBeenCalledOnceWith(42); + expect(result).toBe('flushed'); }); + }); + + + describe('$verifyNoPendingTasks', function() { + var $verifyNoPendingTasks; + var browserDeferVerifySpy; + + beforeEach(inject(function($browser, _$verifyNoPendingTasks_) { + $verifyNoPendingTasks = _$verifyNoPendingTasks_; + browserDeferVerifySpy = spyOn($browser.defer, 'verifyNoPendingTasks').and.returnValue('verified'); + })); + + it('should delegate to `$browser.defer.verifyNoPendingTasks()`', function() { + var result = $verifyNoPendingTasks('fortyTwo'); - it('should throw an exception if there is nothing to be flushed', function() { - expect(function() {browser.defer.flush();}).toThrow('No deferred tasks to be flushed'); + expect(browserDeferVerifySpy).toHaveBeenCalledOnceWith('fortyTwo'); + expect(result).toBe('verified'); }); }); @@ -622,58 +942,84 @@ describe('ngMock', function() { module(function($exceptionHandlerProvider) { expect(function() { $exceptionHandlerProvider.mode('XXX'); - }).toThrow("Unknown mode 'XXX', only 'log'/'rethrow' modes are allowed!"); + }).toThrowError('Unknown mode \'XXX\', only \'log\'/\'rethrow\' modes are allowed!'); }); inject(); // Trigger the tests in `module` }); - }); describe('$timeout', function() { it('should expose flush method that will flush the pending queue of tasks', inject( - function($timeout) { + function($rootScope, $timeout) { var logger = [], logFn = function(msg) { return function() { logger.push(msg); }; }; $timeout(logFn('t1')); $timeout(logFn('t2'), 200); + $rootScope.$evalAsync(logFn('rs')); // Non-timeout tasks are flushed as well. $timeout(logFn('t3')); expect(logger).toEqual([]); $timeout.flush(); - expect(logger).toEqual(['t1', 't3', 't2']); + expect(logger).toEqual(['t1', 'rs', 't3', 't2']); })); - it('should throw an exception when not flushed', inject(function($timeout) { - $timeout(noop); + it('should throw an exception when not flushed', inject(function($rootScope, $timeout) { + $timeout(noop, 100); + $rootScope.$evalAsync(noop); - var expectedError = 'Deferred tasks to flush (1): {id: 0, time: 0}'; - expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(expectedError); + var expectedError = + 'Deferred tasks to flush (2):\n' + + ' {id: 1, type: $evalAsync, time: 0}\n' + + ' {id: 0, type: $timeout, time: 100}'; + expect($timeout.verifyNoPendingTasks).toThrowError(expectedError); })); - it('should do nothing when all tasks have been flushed', inject(function($timeout) { - $timeout(noop); + it('should recommend `$verifyNoPendingTasks()` when all pending tasks are not timeouts', + inject(function($rootScope, $timeout) { + var extraMessage = 'None of the pending tasks are timeouts. If you only want to verify ' + + 'pending timeouts, use `$verifyNoPendingTasks(\'$timeout\')` instead.'; + var errorMessage; + + $timeout(noop, 100); + $rootScope.$evalAsync(noop); + try { $timeout.verifyNoPendingTasks(); } catch (err) { errorMessage = err.message; } + + expect(errorMessage).not.toContain(extraMessage); + + $timeout.flush(100); + $rootScope.$evalAsync(noop); + try { $timeout.verifyNoPendingTasks(); } catch (err) { errorMessage = err.message; } + + expect(errorMessage).toContain(extraMessage); + }) + ); + + + it('should do nothing when all tasks have been flushed', inject(function($rootScope, $timeout) { + $timeout(noop, 100); + $rootScope.$evalAsync(noop); $timeout.flush(); - expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow(); + expect($timeout.verifyNoPendingTasks).not.toThrow(); })); it('should check against the delay if provided within timeout', inject(function($timeout) { $timeout(noop, 100); $timeout.flush(100); - expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow(); + expect($timeout.verifyNoPendingTasks).not.toThrow(); $timeout(noop, 1000); $timeout.flush(100); - expect(function() {$timeout.verifyNoPendingTasks();}).toThrow(); + expect($timeout.verifyNoPendingTasks).toThrow(); $timeout.flush(900); - expect(function() {$timeout.verifyNoPendingTasks();}).not.toThrow(); + expect($timeout.verifyNoPendingTasks).not.toThrow(); })); @@ -690,6 +1036,31 @@ describe('ngMock', function() { $timeout.flush(123); expect(count).toBe(2); })); + + + it('should resolve timeout functions following the timeline', inject(function($timeout) { + var count1 = 0, count2 = 0; + var iterate1 = function() { + count1++; + $timeout(iterate1, 100); + }; + var iterate2 = function() { + count2++; + $timeout(iterate2, 150); + }; + + $timeout(iterate1, 100); + $timeout(iterate2, 150); + $timeout.flush(150); + expect(count1).toBe(1); + expect(count2).toBe(1); + $timeout.flush(50); + expect(count1).toBe(2); + expect(count2).toBe(1); + $timeout.flush(400); + expect(count1).toBe(6); + expect(count2).toBe(4); + })); }); @@ -718,9 +1089,6 @@ describe('ngMock', function() { })); it('should serialize scope that has overridden "hasOwnProperty"', inject(function($rootScope, $sniffer) { - /* jshint -W001 */ - // MS IE8 just doesn't work for this kind of thing, since "for ... in" doesn't return - // things like hasOwnProperty even if it is explicitly defined on the actual object! $rootScope.hasOwnProperty = 'X'; expect(d($rootScope)).toMatch(/Scope\(.*\): \{/); expect(d($rootScope)).toMatch(/hasOwnProperty: "X"/); @@ -741,11 +1109,13 @@ describe('ngMock', function() { var mock = { log: 'module' }; beforeEach(function() { + angular.module('stringRefModule', []).service('stringRef', function() {}); + module({ 'service': mock, 'other': { some: 'replacement'} }, - 'ngResource', + 'stringRefModule', function($provide) { $provide.value('example', 'win'); } ); }); @@ -764,27 +1134,43 @@ describe('ngMock', function() { }); it('should integrate with string and function', function() { - inject(function(service, $resource, example) { + inject(function(service, stringRef, example) { expect(service).toEqual(mock); - expect($resource).toBeDefined(); + expect(stringRef).toBeDefined(); expect(example).toEqual('win'); }); }); - describe('module cleanup', function() { + describe('$inject cleanup', function() { function testFn() { } - it('should add hashKey to module function', function() { - module(testFn); - inject(function() { - expect(testFn.$$hashKey).toBeDefined(); - }); + it('should add $inject when invoking test function', inject(function($injector) { + $injector.invoke(testFn); + expect(testFn.$inject).toBeDefined(); + })); + + it('should cleanup $inject after previous test', function() { + expect(testFn.$inject).toBeUndefined(); + }); + + it('should add $inject when annotating test function', inject(function($injector) { + $injector.annotate(testFn); + expect(testFn.$inject).toBeDefined(); + })); + + it('should cleanup $inject after previous test', function() { + expect(testFn.$inject).toBeUndefined(); }); - it('should cleanup hashKey after previous test', function() { - expect(testFn.$$hashKey).toBeUndefined(); + it('should invoke an already annotated function', inject(function($injector) { + testFn.$inject = []; + $injector.invoke(testFn); + })); + + it('should not cleanup $inject after previous test', function() { + expect(testFn.$inject).toBeDefined(); }); }); }); @@ -800,6 +1186,19 @@ describe('ngMock', function() { }); }); + describe('nested calls', function() { + it('should invoke nested module calls immediately', function() { + module(function($provide) { + $provide.constant('someConst', 'blah'); + module(function(someConst) { + log = someConst; + }); + }); + inject(function() { + expect(log).toBe('blah'); + }); + }); + }); describe('inline in test', function() { it('should load module', function() { @@ -853,36 +1252,12 @@ describe('ngMock', function() { }); }); - - describe('this', function() { - - it('should set `this` to be the jasmine context', inject(function() { - expect(this instanceof jasmine.Spec).toBe(true); - })); - - it('should set `this` to be the jasmine context when inlined in a test', function() { - var tested = false; - - inject(function() { - expect(this instanceof jasmine.Spec).toBe(true); - tested = true; - }); - - expect(tested).toBe(true); - }); - }); - - - // We don't run the following tests on IE8. - // IE8 throws "Object does not support this property or method." error, - // when thrown from a function defined on window (which `inject` is). - it('should not change thrown Errors', inject(function($sniffer) { expect(function() { inject(function() { throw new Error('test message'); }); - }).toThrow('test message'); + }).toThrow(jasmine.objectContaining({message: 'test message'})); })); it('should not change thrown strings', inject(function($sniffer) { @@ -892,43 +1267,111 @@ describe('ngMock', function() { }); }).toThrow('test message'); })); + + describe('error stack trace when called outside of spec context', function() { + // - Chrome, Firefox, Edge give us the stack trace as soon as an Error is created + // - IE10+, PhantomJS give us the stack trace only once the error is thrown + // - IE9 does not provide stack traces + var stackTraceSupported = (function() { + var error = new Error(); + if (!error.stack) { + try { + throw error; + } catch (e) { /* empty */} + } + + return !!error.stack; + })(); + + function testCaller() { + return inject(function injectableError() { + throw new Error(); + }); + } + var throwErrorFromInjectCallback = testCaller(); + + if (stackTraceSupported) { + describe('on browsers supporting stack traces', function() { + it('should update thrown Error stack trace with inject call location', function() { + try { + throwErrorFromInjectCallback(); + } catch (e) { + expect(e.stack).toMatch('injectableError'); + } + }); + }); + } else { + describe('on browsers not supporting stack traces', function() { + it('should not add stack trace information to thrown Error', function() { + try { + throwErrorFromInjectCallback(); + } catch (e) { + expect(e.stack).toBeUndefined(); + } + }); + }); + } + }); + + describe('ErrorAddingDeclarationLocationStack', function() { + it('should be caught by Jasmine\'s `toThrowError()`', function() { + function throwErrorAddingDeclarationStack() { + module(function($provide) { + $provide.factory('badFactory', function() { + throw new Error('BadFactoryError'); + }); + }); + + inject(function(badFactory) {}); + } + + expect(throwErrorAddingDeclarationStack).toThrowError(/BadFactoryError/); + }); + }); }); }); describe('$httpBackend', function() { - var hb, callback, realBackendSpy; + var hb, callback; beforeEach(inject(function($httpBackend) { callback = jasmine.createSpy('callback'); hb = $httpBackend; })); + it('should provide "expect" methods for each HTTP verb', function() { - expect(typeof hb.expectGET).toBe("function"); - expect(typeof hb.expectPOST).toBe("function"); - expect(typeof hb.expectPUT).toBe("function"); - expect(typeof hb.expectPATCH).toBe("function"); - expect(typeof hb.expectDELETE).toBe("function"); - expect(typeof hb.expectHEAD).toBe("function"); + expect(typeof hb.expectGET).toBe('function'); + expect(typeof hb.expectPOST).toBe('function'); + expect(typeof hb.expectPUT).toBe('function'); + expect(typeof hb.expectPATCH).toBe('function'); + expect(typeof hb.expectDELETE).toBe('function'); + expect(typeof hb.expectHEAD).toBe('function'); }); it('should provide "when" methods for each HTTP verb', function() { - expect(typeof hb.whenGET).toBe("function"); - expect(typeof hb.whenPOST).toBe("function"); - expect(typeof hb.whenPUT).toBe("function"); - expect(typeof hb.whenPATCH).toBe("function"); - expect(typeof hb.whenDELETE).toBe("function"); - expect(typeof hb.whenHEAD).toBe("function"); + expect(typeof hb.whenGET).toBe('function'); + expect(typeof hb.whenPOST).toBe('function'); + expect(typeof hb.whenPUT).toBe('function'); + expect(typeof hb.whenPATCH).toBe('function'); + expect(typeof hb.whenDELETE).toBe('function'); + expect(typeof hb.whenHEAD).toBe('function'); + }); + + + it('should provide "route" shortcuts for expect and when', function() { + expect(typeof hb.whenRoute).toBe('function'); + expect(typeof hb.expectRoute).toBe('function'); }); - it('should respond with first matched definition', function() { + it('should respond with first matched definition by default', function() { hb.when('GET', '/url1').respond(200, 'content', {}); hb.when('GET', '/url1').respond(201, 'another', {}); - callback.andCallFake(function(status, response) { + callback.and.callFake(function(status, response) { expect(status).toBe(200); expect(response).toBe('content'); }); @@ -940,12 +1383,84 @@ describe('ngMock', function() { }); + describe('matchLatestDefinitionEnabled()', function() { + + it('should be set to false by default', function() { + expect(hb.matchLatestDefinitionEnabled()).toBe(false); + }); + + + it('should allow to change the value', function() { + hb.matchLatestDefinitionEnabled(true); + expect(hb.matchLatestDefinitionEnabled()).toBe(true); + }); + + + it('should return the httpBackend when used as a setter', function() { + expect(hb.matchLatestDefinitionEnabled(true)).toBe(hb); + }); + + + it('should respond with the first matched definition when false', + function() { + hb.matchLatestDefinitionEnabled(false); + + hb.when('GET', '/url1').respond(200, 'content', {}); + hb.when('GET', '/url1').respond(201, 'another', {}); + + callback.and.callFake(function(status, response) { + expect(status).toBe(200); + expect(response).toBe('content'); + }); + + hb('GET', '/url1', null, callback); + expect(callback).not.toHaveBeenCalled(); + hb.flush(); + expect(callback).toHaveBeenCalledOnce(); + } + ); + + + it('should respond with latest matched definition when true', + function() { + hb.matchLatestDefinitionEnabled(true); + + hb.when('GET', '/url1').respond(200, 'match1', {}); + hb.when('GET', '/url1').respond(200, 'match2', {}); + hb.when('GET', '/url2').respond(204, 'nomatch', {}); + + callback.and.callFake(function(status, response) { + expect(status).toBe(200); + expect(response).toBe('match2'); + }); + + hb('GET', '/url1', null, callback); + + // Check if a newly added match is used + hb.when('GET', '/url1').respond(201, 'match3', {}); + + var callback2 = jasmine.createSpy(); + + callback2.and.callFake(function(status, response) { + expect(status).toBe(201); + expect(response).toBe('match3'); + }); + + hb('GET', '/url1', null, callback2); + expect(callback).not.toHaveBeenCalled(); + hb.flush(); + expect(callback).toHaveBeenCalledOnce(); + } + ); + }); + + it('should respond with a copy of the mock data', function() { var mockObject = {a: 'b'}; hb.when('GET', '/url1').respond(200, mockObject, {}); - callback.andCallFake(function(status, response) { + callback.and.callFake(function(status, response) { expect(status).toBe(200); expect(response).toEqual({a: 'b'}); expect(response).not.toBe(mockObject); @@ -958,7 +1473,7 @@ describe('ngMock', function() { // Fire it again and verify that the returned mock data has not been // modified. - callback.reset(); + callback.calls.reset(); hb('GET', '/url1', null, callback); hb.flush(); expect(callback).toHaveBeenCalledOnce(); @@ -966,11 +1481,68 @@ describe('ngMock', function() { }); + it('should be able to handle Blobs as mock data', function() { + if (typeof Blob !== 'undefined') { + // eslint-disable-next-line no-undef + var mockBlob = new Blob(['{"foo":"bar"}'], {type: 'application/json'}); + + hb.when('GET', '/url1').respond(200, mockBlob, {}); + + callback.and.callFake(function(status, response) { + expect(response).not.toBe(mockBlob); + expect(response.size).toBe(13); + expect(response.type).toBe('application/json'); + expect(response.toString()).toBe('[object Blob]'); + }); + + hb('GET', '/url1', null, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnce(); + } + }); + + it('should throw error when unexpected request', function() { hb.when('GET', '/url1').respond(200, 'content'); expect(function() { hb('GET', '/xxx'); - }).toThrow('Unexpected request: GET /xxx\nNo more request expected'); + }).toThrowError('Unexpected request: GET /xxx\nNo more request expected'); + }); + + + it('should throw error when expectation fails', function() { + expect(function() { + hb.expectPOST('/some', {foo: 1}).respond({}); + hb('POST', '/some', {foo: 2}, callback); + hb.flush(); + }).toThrowError(/^Expected POST \/some with different data/); + }); + + + it('should throw error when expectation about headers fails', function() { + expect(function() { + hb.expectPOST('/some', {foo: 1}, {X: 'val1'}).respond({}); + hb('POST', '/some', {foo: 1}, callback, {X: 'val2'}); + hb.flush(); + }).toThrowError(/^Expected POST \/some with different headers/); + }); + + + it('should throw error about data when expectations about both data and headers fail', function() { + expect(function() { + hb.expectPOST('/some', {foo: 1}, {X: 'val1'}).respond({}); + hb('POST', '/some', {foo: 2}, callback, {X: 'val2'}); + hb.flush(); + }).toThrowError(/^Expected POST \/some with different data/); + }); + + + it('should throw error when response is not defined for a backend definition', function() { + expect(function() { + hb.whenGET('/some'); // no .respond(...) ! + hb('GET', '/some', null, callback); + hb.flush(); + }).toThrowError('No response defined !'); }); @@ -1041,7 +1613,7 @@ describe('ngMock', function() { it('should match only method', function() { hb.when('GET').respond(202, 'c'); - callback.andCallFake(function(status, response) { + callback.and.callFake(function(status, response) { expect(status).toBe(202); expect(response).toBe('c'); }); @@ -1055,18 +1627,109 @@ describe('ngMock', function() { }); - it('should preserve the order of requests', function() { - hb.when('GET', '/url1').respond(200, 'first'); - hb.when('GET', '/url2').respond(201, 'second'); + it('should not error if the url is not provided', function() { + expect(function() { + hb.when('GET'); + + hb.whenGET(); + hb.whenPOST(); + hb.whenPUT(); + hb.whenPATCH(); + hb.whenDELETE(); + hb.whenHEAD(); + + hb.expect('GET'); + + hb.expectGET(); + hb.expectPOST(); + hb.expectPUT(); + hb.expectPATCH(); + hb.expectDELETE(); + hb.expectHEAD(); + }).not.toThrow(); + }); - hb('GET', '/url2', null, callback); - hb('GET', '/url1', null, callback); - hb.flush(); + it('should error if the url is undefined', function() { + expect(function() { + hb.when('GET', undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenGET(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenDELETE(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenJSONP(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenHEAD(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenPATCH(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenPOST(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.whenPUT(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + + expect(function() { + hb.expect('GET', undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.expectGET(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.expectDELETE(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.expectJSONP(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.expectHEAD(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.expectPATCH(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + + expect(function() { + hb.expectPOST(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); - expect(callback.callCount).toBe(2); - expect(callback.argsForCall[0]).toEqual([201, 'second', '', '']); - expect(callback.argsForCall[1]).toEqual([200, 'first', '', '']); + expect(function() { + hb.expectPUT(undefined); + }).toThrowError('Undefined argument `url`; the argument is provided but not defined'); + }); + + + it('should preserve the order of requests', function() { + hb.when('GET', '/url1').respond(200, 'first'); + hb.when('GET', '/url2').respond(201, 'second'); + + hb('GET', '/url2', null, callback); + hb('GET', '/url1', null, callback); + + hb.flush(); + + expect(callback).toHaveBeenCalledTimes(2); + expect(callback.calls.argsFor(0)).toEqual([201, 'second', '', '', 'complete']); + expect(callback.calls.argsFor(1)).toEqual([200, 'first', '', '', 'complete']); }); @@ -1076,11 +1739,11 @@ describe('ngMock', function() { hb('GET', '/url1', undefined, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK'); + expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK', 'complete'); }); it('should default status code to 200', function() { - callback.andCallFake(function(status, response) { + callback.and.callFake(function(status, response) { expect(status).toBe(200); expect(response).toBe('some-data'); }); @@ -1091,7 +1754,7 @@ describe('ngMock', function() { hb('GET', '/url2', null, callback); hb.flush(); expect(callback).toHaveBeenCalled(); - expect(callback.callCount).toBe(2); + expect(callback).toHaveBeenCalledTimes(2); }); it('should default status code to 200 and provide status text', function() { @@ -1099,18 +1762,54 @@ describe('ngMock', function() { hb('GET', '/url1', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK'); + expect(callback).toHaveBeenCalledOnceWith(200, 'first', 'header: val', 'OK', 'complete'); + }); + + it('should default xhrStatus to complete', function() { + callback.and.callFake(function(status, response, headers, x, xhrStatus) { + expect(xhrStatus).toBe('complete'); + }); + + hb.expect('GET', '/url1').respond('some-data'); + hb('GET', '/url1', null, callback); + + hb.flush(); + expect(callback).toHaveBeenCalled(); }); it('should take function', function() { - hb.expect('GET', '/some').respond(function(m, u, d, h) { - return [301, m + u + ';' + d + ';a=' + h.a, {'Connection': 'keep-alive'}, 'Moved Permanently']; + hb.expect('GET', '/some?q=s').respond(function(m, u, d, h, p) { + return [301, m + u + ';' + d + ';a=' + h.a + ';q=' + p.q, {'Connection': 'keep-alive'}, 'Moved Permanently']; + }); + + hb('GET', '/some?q=s', 'data', callback, {a: 'b'}); + hb.flush(); + + expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some?q=s;data;a=b;q=s', 'Connection: keep-alive', 'Moved Permanently', undefined); + }); + + it('should decode query parameters in respond() function', function() { + hb.expect('GET', '/url?query=l%E2%80%A2ng%20string%20w%2F%20spec%5Eal%20char%24&id=1234&orderBy=-name') + .respond(function(m, u, d, h, p) { + return [200, 'id=' + p.id + ';orderBy=' + p.orderBy + ';query=' + p.query]; + }); + + hb('GET', '/url?query=l%E2%80%A2ng%20string%20w%2F%20spec%5Eal%20char%24&id=1234&orderBy=-name', null, callback); + hb.flush(); + + expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;orderBy=-name;query=l•ng string w/ spec^al char$', '', '', undefined); + }); + + it('should include regex captures in respond() params when keys provided', function() { + hb.expect('GET', /\/(.+)\/article\/(.+)/, undefined, undefined, ['id', 'name']) + .respond(function(m, u, d, h, p) { + return [200, 'id=' + p.id + ';name=' + p.name]; }); - hb('GET', '/some', 'data', callback, {a: 'b'}); + hb('GET', '/1234/article/cool-angular-article', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(301, 'GET/some;data;a=b', 'Connection: keep-alive', 'Moved Permanently'); + expect(callback).toHaveBeenCalledOnceWith(200, 'id=1234;name=cool-angular-article', '', '', undefined); }); it('should default response headers to ""', function() { @@ -1122,9 +1821,9 @@ describe('ngMock', function() { hb.flush(); - expect(callback.callCount).toBe(2); - expect(callback.argsForCall[0]).toEqual([200, 'first', '', '']); - expect(callback.argsForCall[1]).toEqual([200, 'second', '', '']); + expect(callback).toHaveBeenCalledTimes(2); + expect(callback.calls.argsFor(0)).toEqual([200, 'first', '', '', 'complete']); + expect(callback.calls.argsFor(1)).toEqual([200, 'second', '', '', 'complete']); }); it('should be able to override response of expect definition', function() { @@ -1134,7 +1833,7 @@ describe('ngMock', function() { hb('GET', '/url1', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', ''); + expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete'); }); it('should be able to override response of when definition', function() { @@ -1144,7 +1843,7 @@ describe('ngMock', function() { hb('GET', '/url1', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', ''); + expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete'); }); it('should be able to override response of expect definition with chaining', function() { @@ -1153,7 +1852,7 @@ describe('ngMock', function() { hb('GET', '/url1', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', ''); + expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete'); }); it('should be able to override response of when definition with chaining', function() { @@ -1162,7 +1861,7 @@ describe('ngMock', function() { hb('GET', '/url1', null, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', ''); + expect(callback).toHaveBeenCalledOnceWith(200, 'second', '', '', 'complete'); }); }); @@ -1174,12 +1873,12 @@ describe('ngMock', function() { expect(function() { hb('GET', '/url2', null, noop, {}); - }).toThrow('Unexpected request: GET /url2\nExpected GET /url1'); + }).toThrowError('Unexpected request: GET /url2\nExpected GET /url1'); }); it('should have precedence over when()', function() { - callback.andCallFake(function(status, response) { + callback.and.callFake(function(status, response) { expect(status).toBe(300); expect(response).toBe('expect'); }); @@ -1193,29 +1892,29 @@ describe('ngMock', function() { }); - it ('should throw exception when only headers differs from expectation', function() { + it('should throw exception when only headers differs from expectation', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', undefined, {'Content-Type': 'application/json'}); expect(function() { hb('GET', '/match', null, noop, {}); - }).toThrow('Expected GET /match with different headers\n' + - 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); + }).toThrowError('Expected GET /match with different headers\n' + + 'EXPECTED: {"Content-Type":"application/json"}\nGOT: {}'); }); - it ('should throw exception when only data differs from expectation', function() { + it('should throw exception when only data differs from expectation', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', 'some-data'); expect(function() { hb('GET', '/match', 'different', noop, {}); - }).toThrow('Expected GET /match with different data\n' + - 'EXPECTED: some-data\nGOT: different'); + }).toThrowError('Expected GET /match with different data\n' + + 'EXPECTED: some-data\nGOT: different'); }); - it ('should not throw an exception when parsed body is equal to expected body object', function() { + it('should not throw an exception when parsed body is equal to expected body object', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', {a: 1, b: 2}); @@ -1230,19 +1929,19 @@ describe('ngMock', function() { }); - it ('should throw exception when only parsed body differs from expected body object', function() { + it('should throw exception when only parsed body differs from expected body object', function() { hb.when('GET').respond(200, '', {}); hb.expect('GET', '/match', {a: 1, b: 2}); expect(function() { hb('GET', '/match', '{"a":1,"b":3}', noop, {}); - }).toThrow('Expected GET /match with different data\n' + - 'EXPECTED: {"a":1,"b":2}\nGOT: {"a":1,"b":3}'); + }).toThrowError('Expected GET /match with different data\n' + + 'EXPECTED: {"a":1,"b":2}\nGOT: {"a":1,"b":3}'); }); - it("should use when's respond() when no expect() respond is defined", function() { - callback.andCallFake(function(status, response) { + it('should use when\'s respond() when no expect() respond is defined', function() { + callback.and.callFake(function(status, response) { expect(status).toBe(201); expect(response).toBe('data'); }); @@ -1278,7 +1977,37 @@ describe('ngMock', function() { hb.flush(2); expect(callback).toHaveBeenCalled(); - expect(callback.callCount).toBe(2); + expect(callback).toHaveBeenCalledTimes(2); + }); + + + it('should flush given number of pending requests beginning at specified request', function() { + var dontCallMe = jasmine.createSpy('dontCallMe'); + + hb.when('GET').respond(200, ''); + hb('GET', '/some', null, dontCallMe); + hb('GET', '/some', null, callback); + hb('GET', '/some', null, callback); + hb('GET', '/some', null, dontCallMe); + + hb.flush(2, 1); + expect(dontCallMe).not.toHaveBeenCalled(); + expect(callback).toHaveBeenCalledTimes(2); + }); + + + it('should flush all pending requests beginning at specified request', function() { + var dontCallMe = jasmine.createSpy('dontCallMe'); + + hb.when('GET').respond(200, ''); + hb('GET', '/some', null, dontCallMe); + hb('GET', '/some', null, dontCallMe); + hb('GET', '/some', null, callback); + hb('GET', '/some', null, callback); + + hb.flush(null, 2); + expect(dontCallMe).not.toHaveBeenCalled(); + expect(callback).toHaveBeenCalledTimes(2); }); @@ -1286,19 +2015,20 @@ describe('ngMock', function() { hb.when('GET').respond(200, ''); hb('GET', '/url', null, callback); - expect(function() {hb.flush(2);}).toThrow('No more pending request to flush !'); + expect(function() {hb.flush(2);}).toThrowError('No more pending request to flush !'); expect(callback).toHaveBeenCalledOnce(); }); it('should throw exception when no request to flush', function() { - expect(function() {hb.flush();}).toThrow('No pending request to flush !'); + expect(function() {hb.flush();}).toThrowError('No pending request to flush !'); hb.when('GET').respond(200, ''); hb('GET', '/some', null, callback); - hb.flush(); + expect(function() {hb.flush(null, 1);}).toThrowError('No pending request to flush !'); - expect(function() {hb.flush();}).toThrow('No pending request to flush !'); + hb.flush(); + expect(function() {hb.flush();}).toThrowError('No pending request to flush !'); }); @@ -1307,7 +2037,7 @@ describe('ngMock', function() { hb.expect('GET', '/url2').respond(); hb('GET', '/url1', null, angular.noop); - expect(function() {hb.flush();}).toThrow('Unsatisfied requests: GET /url2'); + expect(function() {hb.flush();}).toThrowError('Unsatisfied requests: GET /url2'); }); }); @@ -1315,7 +2045,7 @@ describe('ngMock', function() { it('should abort requests when timeout promise resolves', function() { hb.expect('GET', '/url1').respond(200); - var canceler, then = jasmine.createSpy('then').andCallFake(function(fn) { + var canceler, then = jasmine.createSpy('then').and.callFake(function(fn) { canceler = fn; }); @@ -1324,7 +2054,7 @@ describe('ngMock', function() { canceler(); // simulate promise resolution - expect(callback).toHaveBeenCalledWith(-1, undefined, ''); + expect(callback).toHaveBeenCalledWith(-1, undefined, '', undefined, 'abort'); hb.verifyNoOutstandingExpectation(); hb.verifyNoOutstandingRequest(); }); @@ -1336,7 +2066,7 @@ describe('ngMock', function() { hb('GET', '/url1', null, callback, null, 200); $timeout.flush(300); - expect(callback).toHaveBeenCalledWith(-1, undefined, ''); + expect(callback).toHaveBeenCalledWith(-1, undefined, '', undefined, 'timeout'); hb.verifyNoOutstandingExpectation(); hb.verifyNoOutstandingRequest(); })); @@ -1346,7 +2076,7 @@ describe('ngMock', function() { hb.when('GET', '/test'); expect(function() { hb('GET', '/test', null, callback); - }).toThrow('No response defined !'); + }).toThrowError('No response defined !'); }); @@ -1354,7 +2084,7 @@ describe('ngMock', function() { hb.expect('GET', '/url'); expect(function() { hb('GET', '/url', null, callback); - }).toThrow('No response defined !'); + }).toThrowError('No response defined !'); }); @@ -1382,7 +2112,7 @@ describe('ngMock', function() { hb('POST', '/u1', 'ddd', noop, {}); expect(function() {hb.verifyNoOutstandingExpectation();}). - toThrow('Unsatisfied requests: GET /u2, POST /u3'); + toThrowError('Unsatisfied requests: GET /u2, POST /u3'); }); @@ -1405,6 +2135,7 @@ describe('ngMock', function() { }); }); + describe('verifyRequests', function() { it('should throw exception if not all requests were flushed', function() { @@ -1413,7 +2144,35 @@ describe('ngMock', function() { expect(function() { hb.verifyNoOutstandingRequest(); - }).toThrow('Unflushed requests: 1'); + }).toThrowError('Unflushed requests: 1\n' + + ' GET /some'); + }); + + + it('should verify requests fired asynchronously', inject(function($q) { + hb.when('GET').respond(200); + $q.resolve().then(function() { + hb('GET', '/some', null, noop, {}); + }); + + expect(function() { + hb.verifyNoOutstandingRequest(); + }).toThrowError('Unflushed requests: 1\n' + + ' GET /some'); + })); + + + it('should describe multiple unflushed requests', function() { + hb.when('GET').respond(200); + hb.when('PUT').respond(200); + hb('GET', '/some', null, noop, {}); + hb('PUT', '/elsewhere', null, noop, {}); + + expect(function() { + hb.verifyNoOutstandingRequest(); + }).toThrowError('Unflushed requests: 2\n' + + ' GET /some\n' + + ' PUT /elsewhere'); }); }); @@ -1469,13 +2228,68 @@ describe('ngMock', function() { hb[shortcut]('/foo').respond('bar'); hb(method, '/foo', undefined, callback); hb.flush(); - expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '', ''); + expect(callback).toHaveBeenCalledOnceWith(200, 'bar', '', '', 'complete'); }); }); }); }); + describe('expectRoute/whenRoute shortcuts', function() { + angular.forEach(['expectRoute', 'whenRoute'], function(routeShortcut) { + var methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'JSONP']; + they('should provide ' + routeShortcut + ' shortcut with $prop method', methods, + function() { + hb[routeShortcut](this, '/route').respond('path'); + hb(this, '/route', undefined, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '', 'complete'); + } + ); + they('should match colon delimited parameters in ' + routeShortcut + ' $prop method', methods, + function() { + hb[routeShortcut](this, '/route/:id/path/:s_id').respond('path'); + hb(this, '/route/123/path/456', undefined, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnceWith(200, 'path', '', '', 'complete'); + } + ); + they('should ignore query params when matching in ' + routeShortcut + ' $prop method', methods, + function(method) { + angular.forEach([ + {route: '/route1/:id', url: '/route1/Alpha', expectedParams: {id: 'Alpha'}}, + {route: '/route2/:id', url: '/route2/Bravo/?', expectedParams: {id: 'Bravo'}}, + {route: '/route3/:id', url: '/route3/Charlie?q=str&foo=bar', expectedParams: {id: 'Charlie', q: 'str', foo: 'bar'}}, + {route: '/:x/route4', url: '/Delta/route4?q=str&foo=bar', expectedParams: {x: 'Delta', q: 'str', foo: 'bar'}}, + {route: '/route5/:id*', url: '/route5/Echo/456?q=str&foo=bar', expectedParams: {id: 'Echo/456', q: 'str', foo: 'bar'}}, + {route: '/route6/:id*', url: '/route6/Foxtrot/456/?q=str&foo=bar', expectedParams: {id: 'Foxtrot/456', q: 'str', foo: 'bar'}}, + {route: '/route7/:id*', url: '/route7/Golf/456//?q=str&foo=bar', expectedParams: {id: 'Golf/456', q: 'str', foo: 'bar'}}, + {route: '/:x*/route8', url: '/Hotel/123/456/route8/?q=str&foo=bar', expectedParams: {x: 'Hotel/123/456', q: 'str', foo: 'bar'}}, + {route: '/:x*/route9/:id', url: '/India/456/route9/0?q=str&foo=bar', expectedParams: {x: 'India/456', id: '0', q: 'str', foo: 'bar'}}, + {route: '/route10', url: '/route10?q=Juliet&foo=bar', expectedParams: {q: 'Juliet', foo: 'bar'}}, + {route: '/route11', url: '/route11///?q=Kilo', expectedParams: {q: 'Kilo'}}, + {route: '/route12', url: '/route12///', expectedParams: {}} + ], function(testDataEntry) { + callback.calls.reset(); + var paramsSpy = jasmine.createSpy('params'); + hb[routeShortcut](method, testDataEntry.route).respond( + function(method, url, data, headers, params) { + paramsSpy(params); + // status, response, headers, statusText, xhrStatus + return [200, 'path', { 'x-header': 'foo' }, 'OK', 'complete']; + } + ); + hb(method, testDataEntry.url, undefined, callback); + hb.flush(); + expect(callback).toHaveBeenCalledOnceWith(200, 'path', 'x-header: foo', 'OK', 'complete'); + expect(paramsSpy).toHaveBeenCalledOnceWith(testDataEntry.expectedParams); + }); + } + ); + }); + }); + + describe('MockHttpExpectation', function() { /* global MockHttpExpectation */ @@ -1488,6 +2302,11 @@ describe('ngMock', function() { expect(exp.match('GET', 'a/x')).toBe(false); }); + it('should match url with same query params, but different order', function() { + var exp = new MockHttpExpectation('GET', 'www.example.com/x/y?a=b&c=d&e=f'); + + expect(exp.matchUrl('www.example.com/x/y?e=f&c=d&a=b')).toBe(true); + }); it('should accept url as function', function() { var urlValidator = function(url) { @@ -1518,7 +2337,7 @@ describe('ngMock', function() { expect(exp.matchData({})).toBe(false); expect(exp.match('POST', '/url', '{"id": "xxx", "status": "N"}')).toBe(true); - expect(exp.match('POST', '/url', {"id": "xxx", "status": "N"})).toBe(true); + expect(exp.match('POST', '/url', {'id': 'xxx', 'status': 'N'})).toBe(true); }); @@ -1535,7 +2354,7 @@ describe('ngMock', function() { it('should accept headers as function', function() { var exp = new MockHttpExpectation('GET', '/url', undefined, function(h) { - return h['Content-Type'] == 'application/json'; + return h['Content-Type'] === 'application/json'; }); expect(exp.matchHeaders({})).toBe(false); @@ -1549,6 +2368,10 @@ describe('ngMock', function() { it('should create mock application root', inject(function($rootElement) { expect($rootElement.text()).toEqual(''); })); + + it('should attach the `$injector` to `$rootElement`', inject(function($injector, $rootElement) { + expect($rootElement.injector()).toBe($injector); + })); }); @@ -1718,33 +2541,371 @@ describe('ngMock', function() { })); }); }); + + + describe('$controllerDecorator', function() { + + it('should support creating controller with bindings', function() { + var called = false; + var data = [ + { name: 'derp1', id: 0 }, + { name: 'testname', id: 1 }, + { name: 'flurp', id: 2 } + ]; + module(function($controllerProvider) { + $controllerProvider.register('testCtrl', function() { + expect(this.data).toBeUndefined(); + called = true; + }); + }); + inject(function($controller, $rootScope) { + var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data }); + expect(ctrl.data).toBe(data); + expect(called).toBe(true); + }); + }); + + + it('should support assigning bindings when a value is returned from the constructor', + function() { + var called = false; + var data = [ + { name: 'derp1', id: 0 }, + { name: 'testname', id: 1 }, + { name: 'flurp', id: 2 } + ]; + module(function($controllerProvider) { + $controllerProvider.register('testCtrl', function() { + expect(this.data).toBeUndefined(); + called = true; + return {}; + }); + }); + inject(function($controller, $rootScope) { + var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data }); + expect(ctrl.data).toBe(data); + expect(called).toBe(true); + }); + } + ); + + + if (support.classes) { + it('should support assigning bindings to class-based controller', function() { + var called = false; + var data = [ + { name: 'derp1', id: 0 }, + { name: 'testname', id: 1 }, + { name: 'flurp', id: 2 } + ]; + module(function($controllerProvider) { + // eslint-disable-next-line no-eval + var TestCtrl = eval('(class { constructor() { called = true; } })'); + $controllerProvider.register('testCtrl', TestCtrl); + }); + inject(function($controller, $rootScope) { + var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data }); + expect(ctrl.data).toBe(data); + expect(called).toBe(true); + }); + }); + } + }); + + + describe('$componentController', function() { + it('should instantiate a simple controller defined inline in a component', function() { + function TestController($scope, a, b) { + this.$scope = $scope; + this.a = a; + this.b = b; + } + module(function($compileProvider) { + $compileProvider.component('test', { + controller: TestController + }); + }); + inject(function($componentController, $rootScope) { + var $scope = {}; + var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' }); + expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), { x: 'X', y: 'Y' })); + expect($scope.$ctrl).toBe(ctrl); + }); + }); + + it('should instantiate a controller with $$inject annotation defined inline in a component', function() { + function TestController(x, y, z) { + this.$scope = x; + this.a = y; + this.b = z; + } + TestController.$inject = ['$scope', 'a', 'b']; + module(function($compileProvider) { + $compileProvider.component('test', { + controller: TestController + }); + }); + inject(function($componentController, $rootScope) { + var $scope = {}; + var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' }); + expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), { x: 'X', y: 'Y' })); + expect($scope.$ctrl).toBe(ctrl); + }); + }); + + it('should instantiate a named controller defined in a component', function() { + function TestController($scope, a, b) { + this.$scope = $scope; + this.a = a; + this.b = b; + } + module(function($controllerProvider, $compileProvider) { + $controllerProvider.register('TestController', TestController); + $compileProvider.component('test', { + controller: 'TestController' + }); + }); + inject(function($componentController, $rootScope) { + var $scope = {}; + var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' }); + expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), { x: 'X', y: 'Y' })); + expect($scope.$ctrl).toBe(ctrl); + }); + }); + + it('should instantiate a named controller with `controller as` syntax defined in a component', function() { + function TestController($scope, a, b) { + this.$scope = $scope; + this.a = a; + this.b = b; + } + module(function($controllerProvider, $compileProvider) { + $controllerProvider.register('TestController', TestController); + $compileProvider.component('test', { + controller: 'TestController as testCtrl' + }); + }); + inject(function($componentController, $rootScope) { + var $scope = {}; + var ctrl = $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' }); + expect(ctrl).toEqual(extend(new TestController($scope, 'A', 'B'), {x: 'X', y: 'Y'})); + expect($scope.testCtrl).toBe(ctrl); + }); + }); + + it('should instantiate the controller of the restrict:\'E\' component if there are more directives with the same name but not restricted to \'E\'', function() { + function TestController() { + this.r = 6779; + } + module(function($compileProvider) { + $compileProvider.directive('test', function() { + return { restrict: 'A' }; + }); + $compileProvider.component('test', { + controller: TestController + }); + }); + inject(function($componentController, $rootScope) { + var ctrl = $componentController('test', { $scope: {} }); + expect(ctrl).toEqual(new TestController()); + }); + }); + + it('should instantiate the controller of the restrict:\'E\' component if there are more directives with the same name and restricted to \'E\' but no controller', function() { + function TestController() { + this.r = 22926; + } + module(function($compileProvider) { + $compileProvider.directive('test', function() { + return { restrict: 'E' }; + }); + $compileProvider.component('test', { + controller: TestController + }); + }); + inject(function($componentController, $rootScope) { + var ctrl = $componentController('test', { $scope: {} }); + expect(ctrl).toEqual(new TestController()); + }); + }); + + it('should instantiate the controller of the directive with controller, controllerAs and restrict:\'E\' if there are more directives', function() { + function TestController() { + this.r = 18842; + } + module(function($compileProvider) { + $compileProvider.directive('test', function() { + return { }; + }); + $compileProvider.directive('test', function() { + return { + restrict: 'E', + controller: TestController, + controllerAs: '$ctrl' + }; + }); + }); + inject(function($componentController, $rootScope) { + var ctrl = $componentController('test', { $scope: {} }); + expect(ctrl).toEqual(new TestController()); + }); + }); + + it('should fail if there is no directive with restrict:\'E\' and controller', function() { + function TestController() { + this.r = 31145; + } + module(function($compileProvider) { + $compileProvider.directive('test', function() { + return { + restrict: 'AC', + controller: TestController + }; + }); + $compileProvider.directive('test', function() { + return { + restrict: 'E', + controller: TestController + }; + }); + $compileProvider.directive('test', function() { + return { + restrict: 'EA', + controller: TestController, + controllerAs: '$ctrl' + }; + }); + $compileProvider.directive('test', function() { + return { restrict: 'E' }; + }); + }); + inject(function($componentController, $rootScope) { + expect(function() { + $componentController('test', { $scope: {} }); + }).toThrowError('No component found'); + }); + }); + + it('should fail if there more than two components with same name', function() { + function TestController($scope, a, b) { + this.$scope = $scope; + this.a = a; + this.b = b; + } + module(function($compileProvider) { + $compileProvider.directive('test', function() { + return { + restrict: 'E', + controller: TestController, + controllerAs: '$ctrl' + }; + }); + $compileProvider.component('test', { + controller: TestController + }); + }); + inject(function($componentController, $rootScope) { + expect(function() { + var $scope = {}; + $componentController('test', { $scope: $scope, a: 'A', b: 'B' }, { x: 'X', y: 'Y' }); + }).toThrowError('Too many components found'); + }); + }); + + it('should create an isolated child of $rootScope, if no `$scope` local is provided', function() { + function TestController($scope) { + this.$scope = $scope; + } + module(function($compileProvider) { + $compileProvider.component('test', { + controller: TestController + }); + }); + inject(function($componentController, $rootScope) { + var $ctrl = $componentController('test'); + expect($ctrl.$scope).toBeDefined(); + expect($ctrl.$scope.$parent).toBe($rootScope); + // check it is isolated + $rootScope.a = 17; + expect($ctrl.$scope.a).toBeUndefined(); + $ctrl.$scope.a = 42; + expect($rootScope.a).toEqual(17); + }); + }); + }); }); describe('ngMockE2E', function() { + + var noop = angular.noop; + var extend = angular.extend; + describe('$httpBackend', function() { - var hb, realHttpBackend, callback; + var hb, realHttpBackend, realHttpBackendBrowser, $http, callback; beforeEach(function() { - module(function($provide) { - callback = jasmine.createSpy('callback'); + callback = jasmine.createSpy('callback'); + angular.module('ng').config(function($provide) { realHttpBackend = jasmine.createSpy('real $httpBackend'); - $provide.value('$httpBackend', realHttpBackend); - $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); + $provide.factory('$httpBackend', ['$browser', function($browser) { + return realHttpBackend.and.callFake(function() { realHttpBackendBrowser = $browser; }); + }]); }); + module('ngMockE2E'); inject(function($injector) { hb = $injector.get('$httpBackend'); + $http = $injector.get('$http'); }); }); + it('should throw error when unexpected request - without error callback', function() { + expect(function() { + $http.get('/some').then(noop); + + hb.verifyNoOutstandingRequest(); + }).toThrowError('Unexpected request: GET /some\nNo more request expected'); + }); + + + it('should throw error when unexpected request - with error callback', function() { + expect(function() { + $http.get('/some').then(noop, noop); + + hb.verifyNoOutstandingRequest(); + }).toThrowError('Unexpected request: GET /some\nNo more request expected'); + }); + + it('should throw error when expectation fails - without error callback', function() { + expect(function() { + hb.expectPOST('/some', { foo: 1 }).respond({}); + $http.post('/some', { foo: 2 }).then(noop); + + hb.flush(); + }).toThrowError(/^Expected POST \/some with different data/); + }); + + it('should throw error when unexpected request - with error callback', function() { + expect(function() { + hb.expectPOST('/some', { foo: 1 }).respond({}); + $http.post('/some', { foo: 2 }).then(noop, noop); + + hb.flush(); + }).toThrowError(/^Expected POST \/some with different data/); + }); + + describe('passThrough()', function() { it('should delegate requests to the real backend when passThrough is invoked', function() { + var eventHandlers = {progress: angular.noop}; + var uploadEventHandlers = {progress: angular.noop}; + hb.when('GET', /\/passThrough\/.*/).passThrough(); - hb('GET', '/passThrough/23', null, callback, {}, null, true); + hb('GET', '/passThrough/23', null, callback, {}, null, true, 'blob', eventHandlers, uploadEventHandlers); expect(realHttpBackend).toHaveBeenCalledOnceWith( - 'GET', '/passThrough/23', null, callback, {}, null, true); + 'GET', '/passThrough/23', null, callback, {}, null, true, 'blob', eventHandlers, uploadEventHandlers); }); it('should be able to override a respond definition with passThrough', function() { @@ -1753,7 +2914,7 @@ describe('ngMockE2E', function() { hb('GET', '/passThrough/23', null, callback, {}, null, true); expect(realHttpBackend).toHaveBeenCalledOnceWith( - 'GET', '/passThrough/23', null, callback, {}, null, true); + 'GET', '/passThrough/23', null, callback, {}, null, true, undefined, undefined, undefined); }); it('should be able to override a respond definition with passThrough', inject(function($browser) { @@ -1763,7 +2924,15 @@ describe('ngMockE2E', function() { $browser.defer.flush(); expect(realHttpBackend).not.toHaveBeenCalled(); - expect(callback).toHaveBeenCalledOnceWith(200, 'passThrough override', '', ''); + expect(callback).toHaveBeenCalledOnceWith(200, 'passThrough override', '', '', 'complete'); + })); + + it('should pass through to an httpBackend that uses the same $browser service', inject(function($browser) { + hb.when('GET', /\/passThrough\/.*/).passThrough(); + hb('GET', '/passThrough/23'); + + expect(realHttpBackend).toHaveBeenCalledOnce(); + expect(realHttpBackendBrowser).toBe($browser); })); }); @@ -1779,4 +2948,697 @@ describe('ngMockE2E', function() { })); }); }); + + describe('ngAnimateMock', function() { + + beforeEach(module('ngAnimate')); + beforeEach(module('ngAnimateMock')); + + var ss, element, trackedAnimations, animationLog; + + afterEach(function() { + if (element) { + element.remove(); + } + if (ss) { + ss.destroy(); + } + }); + + beforeEach(module(function($animateProvider) { + trackedAnimations = []; + animationLog = []; + + $animateProvider.register('.animate', function() { + return { + leave: logFn('leave'), + addClass: logFn('addClass') + }; + + function logFn(method) { + return function(element) { + animationLog.push('start ' + method); + trackedAnimations.push(getDoneCallback(arguments)); + + return function closingFn(cancel) { + var lab = cancel ? 'cancel' : 'end'; + animationLog.push(lab + ' ' + method); + }; + }; + } + + function getDoneCallback(args) { + for (var i = args.length; i > 0; i--) { + if (angular.isFunction(args[i])) return args[i]; + } + } + }); + + return function($animate, $rootElement, $document, $rootScope) { + ss = createMockStyleSheet($document); + + element = angular.element('
          '); + $rootElement.append(element); + angular.element($document[0].body).append($rootElement); + $animate.enabled(true); + $rootScope.$digest(); + }; + })); + + describe('$animate.queue', function() { + it('should maintain a queue of the executed animations', inject(function($animate) { + element.removeClass('animate'); // we don't care to test any actual animations + var options = {}; + + $animate.addClass(element, 'on', options); + var first = $animate.queue[0]; + expect(first.element).toBe(element); + expect(first.event).toBe('addClass'); + expect(first.options).toBe(options); + + $animate.removeClass(element, 'off', options); + var second = $animate.queue[1]; + expect(second.element).toBe(element); + expect(second.event).toBe('removeClass'); + expect(second.options).toBe(options); + + $animate.leave(element, options); + var third = $animate.queue[2]; + expect(third.element).toBe(element); + expect(third.event).toBe('leave'); + expect(third.options).toBe(options); + })); + }); + + describe('$animate.flush()', function() { + it('should throw an error if there is nothing to animate', inject(function($animate) { + expect(function() { + $animate.flush(); + }).toThrowError('No pending animations ready to be closed or flushed'); + })); + + it('should trigger the animation to start', + inject(function($animate) { + + expect(trackedAnimations.length).toBe(0); + $animate.leave(element); + $animate.flush(); + expect(trackedAnimations.length).toBe(1); + })); + + it('should trigger the animation to end once run and called', + inject(function($animate) { + + $animate.leave(element); + $animate.flush(); + expect(element.parent().length).toBe(1); + + trackedAnimations[0](); + $animate.flush(); + expect(element.parent().length).toBe(0); + })); + + it('should trigger the animation promise callback to fire once run and closed', + inject(function($animate) { + + var doneSpy = jasmine.createSpy(); + $animate.leave(element).then(doneSpy); + $animate.flush(); + + trackedAnimations[0](); + expect(doneSpy).not.toHaveBeenCalled(); + $animate.flush(); + expect(doneSpy).toHaveBeenCalled(); + })); + + it('should trigger a series of CSS animations to trigger and start once run', + inject(function($animate, $rootScope) { + + if (!browserSupportsCssAnimations()) return; + + ss.addRule('.leave-me.ng-leave', 'transition:1s linear all;'); + + var i, elm, elms = []; + for (i = 0; i < 5; i++) { + elm = angular.element('
          '); + element.append(elm); + elms.push(elm); + + $animate.leave(elm); + } + + $rootScope.$digest(); + + for (i = 0; i < 5; i++) { + elm = elms[i]; + expect(elm.hasClass('ng-leave')).toBe(true); + expect(elm.hasClass('ng-leave-active')).toBe(false); + } + + $animate.flush(); + + for (i = 0; i < 5; i++) { + elm = elms[i]; + expect(elm.hasClass('ng-leave')).toBe(true); + expect(elm.hasClass('ng-leave-active')).toBe(true); + } + })); + + it('should trigger parent and child animations to run within the same flush', + inject(function($animate, $rootScope) { + + var child = angular.element('
          '); + element.append(child); + + expect(trackedAnimations.length).toBe(0); + + $animate.addClass(element, 'go'); + $animate.addClass(child, 'start'); + $animate.flush(); + + expect(trackedAnimations.length).toBe(2); + })); + + it('should trigger animation callbacks when called', + inject(function($animate, $rootScope) { + + var spy = jasmine.createSpy(); + $animate.on('addClass', element, spy); + + $animate.addClass(element, 'on'); + expect(spy).not.toHaveBeenCalled(); + + $animate.flush(); + expect(spy).toHaveBeenCalledTimes(1); + + trackedAnimations[0](); + $animate.flush(); + expect(spy).toHaveBeenCalledTimes(2); + })); + }); + + describe('$animate.closeAndFlush()', function() { + it('should close the currently running $animateCss animations', + inject(function($animateCss, $animate) { + + if (!browserSupportsCssAnimations()) return; + + var spy = jasmine.createSpy(); + var runner = $animateCss(element, { + duration: 1, + to: { color: 'red' } + }).start(); + + runner.then(spy); + + expect(spy).not.toHaveBeenCalled(); + $animate.closeAndFlush(); + expect(spy).toHaveBeenCalled(); + })); + + it('should close the currently running $$animateJs animations', + inject(function($$animateJs, $animate) { + + var spy = jasmine.createSpy(); + var runner = $$animateJs(element, 'leave', 'animate', {}).start(); + runner.then(spy); + + expect(spy).not.toHaveBeenCalled(); + $animate.closeAndFlush(); + expect(spy).toHaveBeenCalled(); + })); + + it('should run the closing javascript animation function upon flush', + inject(function($$animateJs, $animate) { + + $$animateJs(element, 'leave', 'animate', {}).start(); + + expect(animationLog).toEqual(['start leave']); + $animate.closeAndFlush(); + expect(animationLog).toEqual(['start leave', 'end leave']); + })); + + it('should not throw when a regular animation has no javascript animation', + inject(function($animate, $$animation, $rootElement) { + + if (!browserSupportsCssAnimations()) return; + + var element = angular.element('
          '); + $rootElement.append(element); + + // Make sure the animation has valid $animateCss options + $$animation(element, null, { + from: { background: 'red' }, + to: { background: 'blue' }, + duration: 1, + transitionStyle: 'all 1s' + }); + + expect(function() { + $animate.closeAndFlush(); + }).not.toThrow(); + + dealoc(element); + })); + + it('should throw an error if there are no animations to close and flush', + inject(function($animate) { + + expect(function() { + $animate.closeAndFlush(); + }).toThrowError('No pending animations ready to be closed or flushed'); + + })); + }); + }); +}); + + +describe('make sure that we can create an injector outside of tests', function() { + //since some libraries create custom injectors outside of tests, + //we want to make sure that this is not breaking the internals of + //how we manage annotated function cleanup during tests. See #10967 + angular.injector([function($injector) {}]); +}); + + +describe('`afterEach` clean-up', function() { + describe('`$rootElement`', function() { + + describe('undecorated', function() { + var prevRootElement; + var prevCleanDataSpy; + + + it('should set up spies for the next test to verify that `$rootElement` was cleaned up', + function() { + module(function($provide) { + $provide.decorator('$rootElement', function($delegate) { + prevRootElement = $delegate; + + // Spy on `angular.element.cleanData()`, so the next test can verify + // that it has been called as necessary + prevCleanDataSpy = spyOn(angular.element, 'cleanData').and.callThrough(); + + return $delegate; + }); + }); + + // Inject the `$rootElement` to ensure it has been created + inject(function($rootElement) { + expect($rootElement.injector()).toBeDefined(); + }); + } + ); + + + it('should clean up `$rootElement` after each test', function() { + // One call is made by `testabilityPatch`'s `dealoc()` + // We want to verify the subsequent call, made by `angular-mocks` + expect(prevCleanDataSpy).toHaveBeenCalledTimes(2); + + var cleanUpNodes = prevCleanDataSpy.calls.argsFor(1)[0]; + expect(cleanUpNodes.length).toBe(1); + expect(cleanUpNodes[0]).toBe(prevRootElement[0]); + }); + }); + + + describe('decorated', function() { + var prevOriginalRootElement; + var prevRootElement; + var prevCleanDataSpy; + + + it('should set up spies for the next text to verify that `$rootElement` was cleaned up', + function() { + module(function($provide) { + $provide.decorator('$rootElement', function($delegate) { + prevOriginalRootElement = $delegate; + + // Mock `$rootElement` to be able to verify that the correct object is cleaned up + prevRootElement = angular.element('
          '); + + // Spy on `angular.element.cleanData()`, so the next test can verify + // that it has been called as necessary + prevCleanDataSpy = spyOn(angular.element, 'cleanData').and.callThrough(); + + return prevRootElement; + }); + }); + + // Inject the `$rootElement` to ensure it has been created + inject(function($rootElement) { + expect($rootElement).toBe(prevRootElement); + expect(prevOriginalRootElement.injector()).toBeDefined(); + expect(prevRootElement.injector()).toBeUndefined(); + + // If we don't clean up `prevOriginalRootElement`-related data now, `testabilityPatch` will + // complain about a memory leak, because it doesn't clean up after the original + // `$rootElement` + // This is a false alarm, because `angular-mocks` would have cleaned up in a subsequent + // `afterEach` block + prevOriginalRootElement.removeData(); + }); + } + ); + + + it('should clean up `$rootElement` (both original and decorated) after each test', + function() { + // One call is made by `testabilityPatch`'s `dealoc()` + // We want to verify the subsequent call, made by `angular-mocks` + expect(prevCleanDataSpy).toHaveBeenCalledTimes(2); + + var cleanUpNodes = prevCleanDataSpy.calls.argsFor(1)[0]; + expect(cleanUpNodes.length).toBe(2); + expect(cleanUpNodes[0]).toBe(prevOriginalRootElement[0]); + expect(cleanUpNodes[1]).toBe(prevRootElement[0]); + } + ); + }); + + + describe('uninstantiated or falsy', function() { + it('should not break if `$rootElement` was never instantiated', function() { + // Just an empty test to verify that `angular-mocks` doesn't break, + // when trying to clean up `$rootElement`, if `$rootElement` was never injected in the test + // (and thus never instantiated/created) + + // Ensure the `$injector` is created - if there is no `$injector`, no clean-up takes places + inject(function() {}); + }); + + + it('should not break if the decorated `$rootElement` is falsy (e.g. `null`)', function() { + module({$rootElement: null}); + + // Ensure the `$injector` is created - if there is no `$injector`, no clean-up takes places + inject(function() {}); + }); + }); + }); + + + describe('`$rootScope`', function() { + describe('undecorated', function() { + var prevRootScope; + var prevDestroySpy; + + + it('should set up spies for the next test to verify that `$rootScope` was cleaned up', + inject(function($rootScope) { + prevRootScope = $rootScope; + prevDestroySpy = spyOn($rootScope, '$destroy').and.callThrough(); + }) + ); + + + it('should clean up `$rootScope` after each test', inject(function($rootScope) { + expect($rootScope).not.toBe(prevRootScope); + expect(prevDestroySpy).toHaveBeenCalledOnce(); + expect(prevRootScope.$$destroyed).toBe(true); + })); + }); + + + describe('falsy or without `$destroy()` method', function() { + it('should not break if `$rootScope` is falsy (e.g. `null`)', function() { + // Just an empty test to verify that `angular-mocks` doesn't break, + // when trying to clean up a mocked `$rootScope` set to `null` + + module({$rootScope: null}); + + // Ensure the `$injector` is created - if there is no `$injector`, no clean-up takes places + inject(function() {}); + }); + + + it('should not break if `$rootScope.$destroy` is not a function', function() { + // Just an empty test to verify that `angular-mocks` doesn't break, + // when trying to clean up a mocked `$rootScope` without a `$destroy()` method + + module({$rootScope: {}}); + + // Ensure the `$injector` is created - if there is no `$injector`, no clean-up takes places + inject(function() {}); + }); + }); + }); +}); + + +describe('sharedInjector', function() { + // this is of a bit tricky feature to test as we hit angular's own testing + // mechanisms (e.g around jQuery cache checking), as ngMock augments the very + // jasmine test runner we're using to test ngMock! + // + // with that in mind, we define a stubbed test framework + // to simulate test cases being run with the ngMock hooks + + + // we use the 'module' and 'inject' globals from ngMock + + it('allows me to mutate a single instance of a module (proving it has been shared)', ngMockTest(function() { + sdescribe('test state is shared', function() { + angular.module('sharedInjectorTestModuleA', []) + .factory('testService', function() { + return { state: 0 }; + }); + + module.sharedInjector(); + + sbeforeAll(module('sharedInjectorTestModuleA')); + + sit('access and mutate', inject(function(testService) { + testService.state += 1; + })); + + sit('expect mutation to have persisted', inject(function(testService) { + expect(testService.state).toEqual(1); + })); + }); + })); + + + it('works with standard beforeEach', ngMockTest(function() { + sdescribe('test state is not shared', function() { + angular.module('sharedInjectorTestModuleC', []) + .factory('testService', function() { + return { state: 0 }; + }); + + sbeforeEach(module('sharedInjectorTestModuleC')); + + sit('access and mutate', inject(function(testService) { + testService.state += 1; + })); + + sit('expect mutation not to have persisted', inject(function(testService) { + expect(testService.state).toEqual(0); + })); + }); + })); + + + it('allows me to stub with shared injector', ngMockTest(function() { + sdescribe('test state is shared', function() { + angular.module('sharedInjectorTestModuleD', []) + .value('testService', 43); + + module.sharedInjector(); + + sbeforeAll(module('sharedInjectorTestModuleD', function($provide) { + $provide.value('testService', 42); + })); + + sit('expected access stubbed value', inject(function(testService) { + expect(testService).toEqual(42); + })); + }); + })); + + it('doesn\'t interfere with other test describes', ngMockTest(function() { + angular.module('sharedInjectorTestModuleE', []) + .factory('testService', function() { + return { state: 0 }; + }); + + sdescribe('with stubbed injector', function() { + + module.sharedInjector(); + + sbeforeAll(module('sharedInjectorTestModuleE')); + + sit('access and mutate', inject(function(testService) { + expect(testService.state).toEqual(0); + testService.state += 1; + })); + + sit('expect mutation to have persisted', inject(function(testService) { + expect(testService.state).toEqual(1); + })); + }); + + sdescribe('without stubbed injector', function() { + sbeforeEach(module('sharedInjectorTestModuleE')); + + sit('access and mutate', inject(function(testService) { + expect(testService.state).toEqual(0); + testService.state += 1; + })); + + sit('expect original, unmutated value', inject(function(testService) { + expect(testService.state).toEqual(0); + })); + }); + })); + + it('prevents nested use of sharedInjector()', function() { + var test = ngMockTest(function() { + sdescribe('outer', function() { + + module.sharedInjector(); + + sdescribe('inner', function() { + + module.sharedInjector(); + + sit('should not get here', function() { + throw Error('should have thrown before here!'); + }); + }); + + }); + + }); + + assertThrowsErrorMatching(test.bind(this), /already called sharedInjector()/); + }); + + it('warns that shared injector cannot be used unless test frameworks define before/after all hooks', function() { + assertThrowsErrorMatching(function() { + module.sharedInjector(); + }, /sharedInjector()/); + }); + + function assertThrowsErrorMatching(fn, re) { + try { + fn(); + } catch (e) { + if (re.test(e.message)) { + return; + } + throw Error('thrown error \'' + e.message + '\' did not match:' + re); + } + throw Error('should have thrown error'); + } + + // run a set of test cases in the sdescribe stub test framework + function ngMockTest(define) { + return function() { + var spec = this; + module.$$currentSpec(null); + + // configure our stubbed test framework and then hook ngMock into it + // in much the same way + module.$$beforeAllHook = sbeforeAll; + module.$$afterAllHook = safterAll; + + sdescribe.root = sdescribe('root', function() {}); + + sdescribe.root.beforeEach.push(module.$$beforeEach); + sdescribe.root.afterEach.push(module.$$afterEach); + + try { + define(); + sdescribe.root.run(); + } finally { + // clear up + module.$$beforeAllHook = null; + module.$$afterAllHook = null; + module.$$currentSpec(spec); + } + }; + } + + // stub test framework that follows the pattern of hooks that + // jasmine/mocha do + function sdescribe(name, define) { + var self = { name: name }; + self.parent = sdescribe.current || sdescribe.root; + if (self.parent) { + self.parent.describes.push(self); + } + + var previous = sdescribe.current; + sdescribe.current = self; + + self.beforeAll = []; + self.beforeEach = []; + self.afterAll = []; + self.afterEach = []; + self.define = define; + self.tests = []; + self.describes = []; + + self.run = function() { + var spec = {}; + self.hooks('beforeAll', spec); + + self.tests.forEach(function(test) { + if (self.parent) self.parent.hooks('beforeEach', spec); + self.hooks('beforeEach', spec); + test.run.call(spec); + self.hooks('afterEach', spec); + if (self.parent) self.parent.hooks('afterEach', spec); + }); + + self.describes.forEach(function(d) { + d.run(); + }); + + self.hooks('afterAll', spec); + }; + + self.hooks = function(hook, spec) { + self[hook].forEach(function(f) { + f.call(spec); + }); + }; + + define(); + + sdescribe.current = previous; + + return self; + } + + function sit(name, fn) { + if (typeof fn !== 'function') throw Error('not fn', fn); + sdescribe.current.tests.push({ + name: name, + run: fn + }); + } + + function sbeforeAll(fn) { + if (typeof fn !== 'function') throw Error('not fn', fn); + sdescribe.current.beforeAll.push(fn); + } + + function safterAll(fn) { + if (typeof fn !== 'function') throw Error('not fn', fn); + sdescribe.current.afterAll.push(fn); + } + + function sbeforeEach(fn) { + if (typeof fn !== 'function') throw Error('not fn', fn); + sdescribe.current.beforeEach.push(fn); + } + + function safterEach(fn) { + if (typeof fn !== 'function') throw Error('not fn', fn); + sdescribe.current.afterEach.push(fn); + } }); diff --git a/test/ngResource/resourceSpec.js b/test/ngResource/resourceSpec.js index ba72525e6de9..bfdac9c0b289 100644 --- a/test/ngResource/resourceSpec.js +++ b/test/ngResource/resourceSpec.js @@ -1,7 +1,11 @@ 'use strict'; -describe("resource", function() { - var $resource, CreditCard, callback, $httpBackend, resourceProvider; +describe('resource', function() { + var noop = angular.noop; + var extend = angular.extend; + +describe('basic usage', function() { + var $resource, CreditCard, callback, $httpBackend, resourceProvider, $q; beforeEach(module('ngResource')); @@ -12,6 +16,7 @@ describe("resource", function() { beforeEach(inject(function($injector) { $httpBackend = $injector.get('$httpBackend'); $resource = $injector.get('$resource'); + $q = $injector.get('$q'); CreditCard = $resource('/CreditCard/:id:verb', {id:'@id.key'}, { charge:{ method:'post', @@ -28,14 +33,14 @@ describe("resource", function() { } }); - callback = jasmine.createSpy(); + callback = jasmine.createSpy('callback'); })); - afterEach(function() { $httpBackend.verifyNoOutstandingExpectation(); }); + describe('isValidDottedPath', function() { /* global isValidDottedPath: false */ it('should support arbitrary dotted names', function() { @@ -44,6 +49,7 @@ describe("resource", function() { expect(isValidDottedPath('1abc')).toBe(false); expect(isValidDottedPath('.')).toBe(false); expect(isValidDottedPath('$')).toBe(true); + expect(isValidDottedPath('@')).toBe(true); expect(isValidDottedPath('a')).toBe(true); expect(isValidDottedPath('A')).toBe(true); expect(isValidDottedPath('a1')).toBe(true); @@ -53,12 +59,14 @@ describe("resource", function() { expect(isValidDottedPath('$.$')).toBe(true); expect(isValidDottedPath('.$')).toBe(false); expect(isValidDottedPath('$.')).toBe(false); + expect(isValidDottedPath('@.')).toBe(false); + expect(isValidDottedPath('.@')).toBe(false); }); }); describe('lookupDottedPath', function() { /* global lookupDottedPath: false */ - var data = {a: {b: 'foo', c: null}}; + var data = {a: {b: 'foo', c: null, '@d':'d-foo'},'@b':'b-foo'}; it('should throw for invalid path', function() { expect(function() { @@ -68,16 +76,18 @@ describe("resource", function() { }); it('should get dotted paths', function() { - expect(lookupDottedPath(data, 'a')).toEqual({b: 'foo', c: null}); + expect(lookupDottedPath(data, 'a')).toEqual({b: 'foo', c: null, '@d':'d-foo'}); expect(lookupDottedPath(data, 'a.b')).toBe('foo'); expect(lookupDottedPath(data, 'a.c')).toBeNull(); + expect(lookupDottedPath(data, 'a.@d')).toBe('d-foo'); + expect(lookupDottedPath(data, '@b')).toBe('b-foo'); }); it('should skip over null/undefined members', function() { - expect(lookupDottedPath(data, 'a.b.c')).toBe(undefined); - expect(lookupDottedPath(data, 'a.c.c')).toBe(undefined); - expect(lookupDottedPath(data, 'a.b.c.d')).toBe(undefined); - expect(lookupDottedPath(data, 'NOT_EXIST')).toBe(undefined); + expect(lookupDottedPath(data, 'a.b.c')).toBeUndefined(); + expect(lookupDottedPath(data, 'a.c.c')).toBeUndefined(); + expect(lookupDottedPath(data, 'a.b.c.d')).toBeUndefined(); + expect(lookupDottedPath(data, 'NOT_EXIST')).toBeUndefined(); }); }); @@ -90,8 +100,78 @@ describe("resource", function() { $httpBackend.flush(); }); + it('should include a request body when calling custom method with hasBody is true', function() { + var instant = {name: 'info.txt'}; + var condition = {at: '2038-01-19 03:14:08'}; + + $httpBackend.expect('CREATE', '/fooresource', instant).respond({fid: 42}); + $httpBackend.expect('DELETE', '/fooresource', condition).respond({}); + + var r = $resource('/fooresource', {}, { + create: {method: 'CREATE', hasBody: true}, + delete: {method: 'DELETE', hasBody: true} + }); + + var creationResponse = r.create(instant); + var deleteResponse = r.delete(condition); + + $httpBackend.flush(); + + expect(creationResponse.fid).toBe(42); + expect(deleteResponse.$resolved).toBe(true); + }); + + it('should not include a request body if hasBody is false on POST, PUT and PATCH', function() { + function verifyRequest(method, url, data) { + expect(data).toBeUndefined(); + return [200, {id: 42}]; + } + + $httpBackend.expect('POST', '/foo').respond(verifyRequest); + $httpBackend.expect('PUT', '/foo').respond(verifyRequest); + $httpBackend.expect('PATCH', '/foo').respond(verifyRequest); + + var R = $resource('/foo', {}, { + post: {method: 'POST', hasBody: false}, + put: {method: 'PUT', hasBody: false}, + patch: {method: 'PATCH', hasBody: false} + }); + + var postResponse = R.post(); + var putResponse = R.put(); + var patchResponse = R.patch(); + + $httpBackend.flush(); + + expect(postResponse.id).toBe(42); + expect(putResponse.id).toBe(42); + expect(patchResponse.id).toBe(42); + }); + + it('should expect a body if hasBody is true', function() { + var username = 'yathos'; + var loginRequest = {name: username, password: 'Smile'}; + var user = {id: 1, name: username}; + + $httpBackend.expect('LOGIN', '/user/me', loginRequest).respond(user); + + $httpBackend.expect('LOGOUT', '/user/me', null).respond(null); - it("should build resource", function() { + var UserService = $resource('/user/me', {}, { + login: {method: 'LOGIN', hasBody: true}, + logout: {method: 'LOGOUT', hasBody: false} + }); + + var loginResponse = UserService.login(loginRequest); + var logoutResponse = UserService.logout(); + + $httpBackend.flush(); + + expect(loginResponse.id).toBe(user.id); + expect(logoutResponse.$resolved).toBe(true); + }); + + it('should build resource', function() { expect(typeof CreditCard).toBe('function'); expect(typeof CreditCard.get).toBe('function'); expect(typeof CreditCard.save).toBe('function'); @@ -133,25 +213,42 @@ describe("resource", function() { it('should omit properties from prototype chain', function() { var original, clone = {}; function Func() {} - Func.prototype.hello = "world"; + Func.prototype.hello = 'world'; original = new Func(); - original.goodbye = "world"; + original.goodbye = 'world'; expect(shallowClearAndCopy(original, clone)).toBe(clone); expect(clone.hello).toBeUndefined(); - expect(clone.goodbye).toBe("world"); + expect(clone.goodbye).toBe('world'); }); }); + it('should not throw if response.data is the resource object', function() { + var data = {id:{key:123}, number:'9876'}; + $httpBackend.expect('GET', '/CreditCard/123').respond(data); + + var cc = CreditCard.get({id:123}); + $httpBackend.flush(); + expect(cc instanceof CreditCard).toBe(true); + + $httpBackend.expect('POST', '/CreditCard/123', angular.toJson(data)).respond(cc); + + cc.$save(); + $httpBackend.flush(); + expect(cc.id).toEqual({key:123}); + expect(cc.number).toEqual('9876'); + }); + + it('should default to empty parameters', function() { $httpBackend.expect('GET', 'URL').respond({}); $resource('URL').query(); }); - it('should ignore slashes of undefinend parameters', function() { + it('should ignore slashes of undefined parameters', function() { var R = $resource('/Path/:a/:b/:c'); $httpBackend.when('GET', '/Path').respond('{}'); @@ -176,7 +273,7 @@ describe("resource", function() { R.get({a:6, b:7, c:8}); }); - it('should not ignore leading slashes of undefinend parameters that have non-slash trailing sequence', function() { + it('should not ignore leading slashes of undefined parameters that have non-slash trailing sequence', function() { var R = $resource('/Path/:a.foo/:b.bar/:c.baz'); $httpBackend.when('GET', '/Path/.foo/.bar.baz').respond('{}'); @@ -230,14 +327,18 @@ describe("resource", function() { $httpBackend.expect('GET', '/Path/foo%231').respond('{}'); $httpBackend.expect('GET', '/Path/doh!@foo?bar=baz%231').respond('{}'); $httpBackend.expect('GET', '/Path/herp$').respond('{}'); + $httpBackend.expect('GET', '/Path/foo;bar').respond('{}'); + $httpBackend.expect('GET', '/Path/foo?bar=baz;qux').respond('{}'); R.get({a: 'foo#1'}); R.get({a: 'doh!@foo', bar: 'baz#1'}); R.get({a: 'herp$'}); + R.get({a: 'foo;bar'}); + R.get({a: 'foo', bar: 'baz;qux'}); }); it('should not encode @ in url params', function() { - //encodeURIComponent is too agressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt + //encodeURIComponent is too aggressive and doesn't follow http://www.ietf.org/rfc/rfc3986.txt //with regards to the character set (pchar) allowed in path segments //so we need this test to make sure that we don't over-encode the params and break stuff like //buzz api which uses @self @@ -292,6 +393,38 @@ describe("resource", function() { R.get({a: 'foo'}); }); + it('should support IPv6 URLs', function() { + test('http://[2620:0:861:ed1a::1]', {ed1a: 'foo'}, 'http://[2620:0:861:ed1a::1]'); + test('http://[2620:0:861:ed1a::1]/', {ed1a: 'foo'}, 'http://[2620:0:861:ed1a::1]/'); + test('http://[2620:0:861:ed1a::1]/:ed1a', {ed1a: 'foo'}, 'http://[2620:0:861:ed1a::1]/foo'); + test('http://[2620:0:861:ed1a::1]/:ed1a', {}, 'http://[2620:0:861:ed1a::1]/'); + test('http://[2620:0:861:ed1a::1]/:ed1a/', {ed1a: 'foo'}, 'http://[2620:0:861:ed1a::1]/foo/'); + test('http://[2620:0:861:ed1a::1]/:ed1a/', {}, 'http://[2620:0:861:ed1a::1]/'); + + // Helpers + function test(templateUrl, params, actualUrl) { + var R = $resource(templateUrl, null, null, {stripTrailingSlashes: false}); + $httpBackend.expect('GET', actualUrl).respond(null); + R.get(params); + } + }); + + it('should support params in the `hostname` part of the URL', function() { + test('http://:hostname', {hostname: 'foo.com'}, 'http://foo.com'); + test('http://:hostname/', {hostname: 'foo.com'}, 'http://foo.com/'); + test('http://:l2Domain.:l1Domain', {l1Domain: 'com', l2Domain: 'bar'}, 'http://bar.com'); + test('http://:l2Domain.:l1Domain/', {l1Domain: 'com', l2Domain: 'bar'}, 'http://bar.com/'); + test('http://127.0.0.:octet', {octet: 42}, 'http://127.0.0.42'); + test('http://127.0.0.:octet/', {octet: 42}, 'http://127.0.0.42/'); + + // Helpers + function test(templateUrl, params, actualUrl) { + var R = $resource(templateUrl, null, null, {stripTrailingSlashes: false}); + $httpBackend.expect('GET', actualUrl).respond(null); + R.get(params); + } + }); + it('should support overriding provider default trailing-slash stripping configuration', function() { // Set the new behavior for all new resources created by overriding the // provider configuration @@ -318,10 +451,18 @@ describe("resource", function() { }); - it('should encode & in url params', function() { - var R = $resource('/Path/:a'); + it('should encode & in query params unless in query param value', function() { + var R1 = $resource('/Path/:a'); $httpBackend.expect('GET', '/Path/doh&foo?bar=baz%261').respond('{}'); - R.get({a: 'doh&foo', bar: 'baz&1'}); + R1.get({a: 'doh&foo', bar: 'baz&1'}); + + var R2 = $resource('/api/myapp/resource?:query'); + $httpBackend.expect('GET', '/api/myapp/resource?foo&bar').respond('{}'); + R2.get({query: 'foo&bar'}); + + var R3 = $resource('/api/myapp/resource?from=:from'); + $httpBackend.expect('GET', '/api/myapp/resource?from=bar%20%26%20blanks').respond('{}'); + R3.get({from: 'bar & blanks'}); }); @@ -359,7 +500,7 @@ describe("resource", function() { }); - it("should build resource with action default param overriding default param", function() { + it('should build resource with action default param overriding default param', function() { $httpBackend.expect('GET', '/Customer/123').respond({id: 'abc'}); var TypeItem = $resource('/:type/:typeId', {type: 'Order'}, {get: {method: 'GET', params: {type: 'Customer'}}}); @@ -404,11 +545,11 @@ describe("resource", function() { it('should throw an exception if a param is called "hasOwnProperty"', function() { expect(function() { $resource('/:hasOwnProperty').get(); - }).toThrowMinErr('$resource','badname', "hasOwnProperty is not a valid parameter name"); + }).toThrowMinErr('$resource','badname', 'hasOwnProperty is not a valid parameter name'); }); - it("should create resource", function() { + it('should create resource', function() { $httpBackend.expect('POST', '/CreditCard', '{"name":"misko"}').respond({id: 123, name: 'misko'}); var cc = CreditCard.save({name: 'misko'}, callback); @@ -418,12 +559,12 @@ describe("resource", function() { $httpBackend.flush(); expect(cc).toEqualData({id: 123, name: 'misko'}); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0]).toEqual(cc); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(cc); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); - it("should read resource", function() { + it('should read resource', function() { $httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'}); var cc = CreditCard.get({id: 123}, callback); @@ -433,21 +574,21 @@ describe("resource", function() { $httpBackend.flush(); expect(cc).toEqualData({id: 123, number: '9876'}); - expect(callback.mostRecentCall.args[0]).toEqual(cc); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(cc); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); it('should send correct headers', function() { $httpBackend.expectPUT('/CreditCard/123', undefined, function(headers) { - return headers['If-None-Match'] == "*"; + return headers['If-None-Match'] === '*'; }).respond({id:123}); CreditCard.conditionalPut({id: {key:123}}); }); - it("should read partial resource", function() { + it('should read partial resource', function() { $httpBackend.expect('GET', '/CreditCard').respond([{id:{key:123}}]); var ccs = CreditCard.query(); @@ -461,13 +602,13 @@ describe("resource", function() { $httpBackend.expect('GET', '/CreditCard/123').respond({id: {key: 123}, number: '9876'}); cc.$get(callback); $httpBackend.flush(); - expect(callback.mostRecentCall.args[0]).toEqual(cc); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(cc); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); expect(cc.number).toEqual('9876'); }); - it("should update resource", function() { + it('should update resource', function() { $httpBackend.expect('POST', '/CreditCard/123', '{"id":{"key":123},"name":"misko"}'). respond({id: {key: 123}, name: 'rama'}); @@ -478,7 +619,7 @@ describe("resource", function() { }); - it("should query resource", function() { + it('should query resource', function() { $httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]); var ccs = CreditCard.query({key: 'value'}, callback); @@ -487,12 +628,12 @@ describe("resource", function() { $httpBackend.flush(); expect(ccs).toEqualData([{id:1}, {id:2}]); - expect(callback.mostRecentCall.args[0]).toEqual(ccs); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(ccs); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); - it("should have all arguments optional", function() { + it('should have all arguments optional', function() { $httpBackend.expect('GET', '/CreditCard').respond([{id:1}]); var log = ''; @@ -510,17 +651,17 @@ describe("resource", function() { expect(callback).not.toHaveBeenCalled(); $httpBackend.flush(); - expect(callback.mostRecentCall.args[0]).toEqualData({}); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqualData({}); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); - callback.reset(); + callback.calls.reset(); $httpBackend.expect('DELETE', '/CreditCard/333').respond(204, null); CreditCard.remove({id:333}, callback); expect(callback).not.toHaveBeenCalled(); $httpBackend.flush(); - expect(callback.mostRecentCall.args[0]).toEqualData({}); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqualData({}); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); @@ -539,7 +680,7 @@ describe("resource", function() { }); - it("should patch a resource", function() { + it('should patch a resource', function() { $httpBackend.expectPATCH('/CreditCard/123', '{"name":"igor"}'). respond({id: 123, name: 'rama'}); @@ -568,8 +709,8 @@ describe("resource", function() { $httpBackend.flush(); expect(cc).toEqualData({id:123}); - expect(callback.mostRecentCall.args[0]).toEqual(cc); - expect(callback.mostRecentCall.args[1]()).toEqual({header1: 'a'}); + expect(callback.calls.mostRecent().args[0]).toEqual(cc); + expect(callback.calls.mostRecent().args[1]()).toEqual(extend(Object.create(null), {header1: 'a'})); }); @@ -590,20 +731,10 @@ describe("resource", function() { }); - it('should bind default parameters', function() { - $httpBackend.expect('GET', '/CreditCard/123.visa?minimum=0.05').respond({id: 123}); - var Visa = CreditCard.bind({verb:'.visa', minimum:0.05}); - var visa = Visa.get({id:123}); - $httpBackend.flush(); - expect(visa).toEqualData({id:123}); - }); - - it('should support dynamic default parameters (global)', function() { var currentGroup = 'students', Person = $resource('/Person/:group/:id', { group: function() { return currentGroup; }}); - $httpBackend.expect('GET', '/Person/students/fedor').respond({id: 'fedor', email: 'f@f.com'}); var fedor = Person.get({id: 'fedor'}); @@ -613,6 +744,32 @@ describe("resource", function() { }); + it('should pass resource object to dynamic default parameters', function() { + var Person = $resource('/Person/:id', { + id: function(data) { + return data ? data.id : 'fedor'; + } + }); + + $httpBackend.expect('GET', '/Person/fedor').respond( + {id: 'fedor', email: 'f@f.com', count: 1}); + + var fedor = Person.get(); + $httpBackend.flush(); + + expect(fedor).toEqualData({id: 'fedor', email: 'f@f.com', count: 1}); + + $httpBackend.expect('POST', '/Person/fedor2').respond( + {id: 'fedor2', email: 'f2@f.com', count: 2}); + + fedor.id = 'fedor2'; + fedor.$save(); + $httpBackend.flush(); + + expect(fedor).toEqualData({id: 'fedor2', email: 'f2@f.com', count: 2}); + }); + + it('should support dynamic default parameters (action specific)', function() { var currentGroup = 'students', Person = $resource('/Person/:group/:id', {}, { @@ -669,6 +826,24 @@ describe("resource", function() { expect(json).toEqual({id: 123, number: '9876', $myProp: 'still here'}); }); + it('should not include $cancelRequest when resource is toJson\'ed', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + cancellable: true + } + }); + + var card = CreditCard.get(); + var json = card.toJSON(); + + expect(card.$cancelRequest).toBeDefined(); + expect(json.$cancelRequest).toBeUndefined(); + }); + + describe('promise api', function() { var $rootScope; @@ -691,7 +866,7 @@ describe("resource", function() { $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0]).toBe(cc); + expect(callback.calls.mostRecent().args[0]).toBe(cc); }); @@ -702,7 +877,7 @@ describe("resource", function() { cc.$promise.then(callback); $httpBackend.flush(); - callback.reset(); + callback.calls.reset(); cc.$promise.then(callback); $rootScope.$apply(); //flush async queue @@ -743,7 +918,7 @@ describe("resource", function() { cc.$promise.then(null, callback); $httpBackend.flush(); - var response = callback.mostRecentCall.args[0]; + var response = callback.calls.mostRecent().args[0]; expect(response.data).toEqual('resource not found'); expect(response.status).toEqual(404); @@ -801,7 +976,7 @@ describe("resource", function() { $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); expect(cc).toEqualData({id: 123, number: '9876'}); - callback.reset(); + callback.calls.reset(); $httpBackend.expect('POST', '/CreditCard').respond({id: 1, number: '9'}); @@ -838,6 +1013,7 @@ describe("resource", function() { expect(cc.url).toBe('/new-id'); }); + it('should pass the same transformed value to success callbacks and to promises', function() { $httpBackend.expect('GET', '/CreditCard').respond(200, { value: 'original' }); @@ -884,7 +1060,7 @@ describe("resource", function() { $httpBackend.flush(); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0]).toBe(ccs); + expect(callback.calls.mostRecent().args[0]).toBe(ccs); }); @@ -895,7 +1071,7 @@ describe("resource", function() { ccs.$promise.then(callback); $httpBackend.flush(); - callback.reset(); + callback.calls.reset(); ccs.$promise.then(callback); $rootScope.$apply(); //flush async queue @@ -922,7 +1098,7 @@ describe("resource", function() { ccs.$promise.then(null, callback); $httpBackend.flush(); - var response = callback.mostRecentCall.args[0]; + var response = callback.calls.mostRecent().args[0]; expect(response.data).toEqual('resource not found'); expect(response.status).toEqual(404); @@ -955,71 +1131,477 @@ describe("resource", function() { }); }); - it('should allow per action response interceptor that gets full response', function() { - CreditCard = $resource('/CreditCard', {}, { - query: { - method: 'get', - isArray: true, - interceptor: { - response: function(response) { - return response; + + describe('requestInterceptor', function() { + var rejectReason = {'lol':'cat'}; + var successSpy, failureSpy; + + beforeEach(function() { + successSpy = jasmine.createSpy('successSpy'); + failureSpy = jasmine.createSpy('failureSpy'); + }); + + it('should allow per action request interceptor that gets full configuration', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function(httpConfig) { + callback(httpConfig); + return httpConfig; + } } } - } + }); + + $httpBackend.expect('GET', '/CreditCard').respond([{id: 1}]); + + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); + + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnce(); + expect(successSpy).toHaveBeenCalledOnce(); + expect(failureSpy).not.toHaveBeenCalled(); + + expect(callback).toHaveBeenCalledWith({ + 'method': 'get', + 'url': '/CreditCard' + }); }); - $httpBackend.expect('GET', '/CreditCard').respond([{id: 1}]); + it('should call $http with the value returned from requestInterceptor', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function(httpConfig) { + httpConfig.url = '/DebitCard'; + return httpConfig; + } + } + } + }); - var ccs = CreditCard.query(); + $httpBackend.expect('GET', '/DebitCard').respond([{id: 1}]); - ccs.$promise.then(callback); + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); - $httpBackend.flush(); - expect(callback).toHaveBeenCalledOnce(); + $httpBackend.flush(); + expect(successSpy).toHaveBeenCalledOnceWith(jasmine.arrayContaining([ + jasmine.objectContaining({id: 1}) + ])); + expect(failureSpy).not.toHaveBeenCalled(); + }); - var response = callback.mostRecentCall.args[0]; - expect(response.resource).toBe(ccs); - expect(response.status).toBe(200); - expect(response.config).toBeDefined(); - }); + it('should abort the operation if the requestInterceptor rejects the operation', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function() { + return $q.reject(rejectReason); + } + } + } + }); + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); - it('should allow per action responseError interceptor that gets full response', function() { - CreditCard = $resource('/CreditCard', {}, { - query: { - method: 'get', - isArray: true, - interceptor: { - responseError: function(response) { - return response; + // Make sure all promises resolve. + $rootScope.$apply(); + + // Ensure the resource promise was rejected + expect(resource.$resolved).toBeTruthy(); + expect(successSpy).not.toHaveBeenCalled(); + expect(failureSpy).toHaveBeenCalledOnceWith(rejectReason); + + // Ensure that no requests were made. + $httpBackend.verifyNoOutstandingRequest(); + }); + + it('should call requestErrorInterceptor if requestInterceptor rejects the operation', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function() { + return $q.reject(rejectReason); + }, + requestError: function(rejection) { + callback(rejection); + return $q.reject(rejection); + } } } + }); + + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); + $rootScope.$digest(); + + expect(callback).toHaveBeenCalledOnceWith(rejectReason); + expect(successSpy).not.toHaveBeenCalled(); + expect(failureSpy).toHaveBeenCalledOnceWith(rejectReason); + + // Ensure that no requests were made. + $httpBackend.verifyNoOutstandingRequest(); + }); + + it('should abort the operation if a requestErrorInterceptor rejects the operation', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function() { + return $q.reject(rejectReason); + }, + requestError: function(rejection) { + return $q.reject(rejection); + } + } + } + }); + + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); + $rootScope.$apply(); + + expect(resource.$resolved).toBeTruthy(); + expect(successSpy).not.toHaveBeenCalled(); + expect(failureSpy).toHaveBeenCalledOnceWith(rejectReason); + + // Ensure that no requests were made. + $httpBackend.verifyNoOutstandingRequest(); + }); + + it('should continue the operation if a requestErrorInterceptor rescues it', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function(httpConfig) { + return $q.reject(httpConfig); + }, + requestError: function(httpConfig) { + return $q.resolve(httpConfig); + } + } + } + }); + + $httpBackend.expect('GET', '/CreditCard').respond([{id: 1}]); + + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); + $httpBackend.flush(); + + expect(resource.$resolved).toBeTruthy(); + expect(successSpy).toHaveBeenCalledOnceWith(jasmine.arrayContaining([ + jasmine.objectContaining({id: 1}) + ])); + expect(failureSpy).not.toHaveBeenCalled(); + + $httpBackend.verifyNoOutstandingRequest(); + }); + }); + + + describe('responseInterceptor', function() { + it('should allow per action response interceptor that gets full response', function() { + var response; + + $httpBackend.expect('GET', '/CreditCard').respond(201, {id: 1}, {foo: 'bar'}, 'Ack'); + CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'get', + interceptor: {response: function(resp) { response = resp; }} + } + }); + + var cc = CreditCard.get(); + $httpBackend.flush(); + + expect(response.resource).toBe(cc); + expect(response.config).toBeDefined(); + expect(response.status).toBe(201); + expect(response.statusText).toBe('Ack'); + expect(response.headers()).toEqual({foo: 'bar'}); + }); + + + it('should allow per action responseError interceptor that gets full response', function() { + var response; + + $httpBackend.expect('GET', '/CreditCard').respond(404, {ignored: 'stuff'}, {foo: 'bar'}, 'Ack'); + CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'get', + interceptor: {responseError: function(resp) { response = resp; }} + } + }); + + var cc = CreditCard.get(); + $httpBackend.flush(); + + expect(response.resource).toBe(cc); + expect(response.config).toBeDefined(); + expect(response.status).toBe(404); + expect(response.statusText).toBe('Ack'); + expect(response.headers()).toEqual({foo: 'bar'}); + }); + + + it('should fulfill the promise with the value returned by the response interceptor', + function() { + $httpBackend.whenGET('/CreditCard').respond(200); + CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'get', + interceptor: {response: function() { return 'foo'; }} + }, + test2: { + method: 'get', + interceptor: {response: function() { return $q.resolve('bar'); }} + }, + test3: { + method: 'get', + interceptor: {response: function() { return $q.reject('baz'); }} + } + }); + + CreditCard.test1().$promise.then(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('foo'); + + callback.calls.reset(); + + CreditCard.test2().$promise.then(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('bar'); + + callback.calls.reset(); + + CreditCard.test3().$promise.then(null, callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('baz'); + } + ); + + + it('should fulfill the promise with the value returned by the responseError interceptor', + function() { + $httpBackend.whenGET('/CreditCard').respond(404); + CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'get', + interceptor: {responseError: function() { return 'foo'; }} + }, + test2: { + method: 'get', + interceptor: {responseError: function() { return $q.resolve('bar'); }} + }, + test3: { + method: 'get', + interceptor: {responseError: function() { return $q.reject('baz'); }} + } + }); + + CreditCard.test1().$promise.then(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('foo'); + + callback.calls.reset(); + + CreditCard.test2().$promise.then(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('bar'); + + callback.calls.reset(); + + CreditCard.test3().$promise.then(null, callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('baz'); } + ); + + + it('should call the success callback when response interceptor succeeds', function() { + $httpBackend.whenGET('/CreditCard').respond(200); + CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'get', + interceptor: {response: function() { return 'foo'; }} + }, + test2: { + method: 'get', + interceptor: {response: function() { return $q.resolve('bar'); }} + } + }); + + CreditCard.test1(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('foo', jasmine.any(Function), 200, ''); + + callback.calls.reset(); + + CreditCard.test2(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('bar', jasmine.any(Function), 200, ''); }); - $httpBackend.expect('GET', '/CreditCard').respond(404); - var ccs = CreditCard.query(); + it('should call the error callback when response interceptor fails', function() { + $httpBackend.whenGET('/CreditCard').respond(200); + CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'get', + interceptor: {response: function() { throw 'foo'; }} + }, + test2: { + method: 'get', + interceptor: {response: function() { return $q.reject('bar'); }} + } + }); + + CreditCard.test1(noop, callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('foo'); + + callback.calls.reset(); - ccs.$promise.then(callback); + CreditCard.test2(noop, callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('bar'); + }); - $httpBackend.flush(); - expect(callback).toHaveBeenCalledOnce(); - var response = callback.mostRecentCall.args[0]; - expect(response.status).toBe(404); - expect(response.config).toBeDefined(); + it('should call the success callback when responseError interceptor succeeds', function() { + $httpBackend.whenGET('/CreditCard').respond(404); + CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'get', + interceptor: {responseError: function() { return 'foo'; }} + }, + test2: { + method: 'get', + interceptor: {responseError: function() { return $q.resolve('bar'); }} + } + }); + + CreditCard.test1(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('foo', jasmine.any(Function), 404, ''); + + callback.calls.reset(); + + CreditCard.test2(callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('bar', jasmine.any(Function), 404, ''); + }); + + + it('should call the error callback when responseError interceptor fails', function() { + $httpBackend.whenGET('/CreditCard').respond(404); + CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'get', + interceptor: {responseError: function() { throw 'foo'; }} + }, + test2: { + method: 'get', + interceptor: {responseError: function() { return $q.reject('bar'); }} + } + }); + + CreditCard.test1(noop, callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('foo'); + + callback.calls.reset(); + + CreditCard.test2(noop, callback); + $httpBackend.flush(); + expect(callback).toHaveBeenCalledOnceWith('bar'); + }); }); }); + describe('success mode', function() { + it('should call the success callback (as 1st argument) on 2xx responses', function() { + var instance, headers, status, statusText; + var successCb = jasmine.createSpy('successCb').and.callFake(function(d, h, s, t) { + expect(d).toBe(instance); + expect(h()).toEqual(jasmine.objectContaining(headers)); + expect(s).toBe(status); + expect(t).toBe(statusText); + }); + + instance = CreditCard.get(successCb); + headers = {foo: 'bar'}; + status = 200; + statusText = 'OK'; + $httpBackend.expect('GET', '/CreditCard').respond(status, {}, headers, statusText); + $httpBackend.flush(); + + expect(successCb).toHaveBeenCalledOnce(); + + instance = CreditCard.get(successCb); + headers = {baz: 'qux'}; + status = 299; + statusText = 'KO'; + $httpBackend.expect('GET', '/CreditCard').respond(status, {}, headers, statusText); + $httpBackend.flush(); + + expect(successCb).toHaveBeenCalledTimes(2); + }); + + + it('should call the success callback (as 2nd argument) on 2xx responses', function() { + var instance, headers, status, statusText; + var successCb = jasmine.createSpy('successCb').and.callFake(function(d, h, s, t) { + expect(d).toBe(instance); + expect(h()).toEqual(jasmine.objectContaining(headers)); + expect(s).toBe(status); + expect(t).toBe(statusText); + }); + + instance = CreditCard.get({id: 123}, successCb); + headers = {foo: 'bar'}; + status = 200; + statusText = 'OK'; + $httpBackend.expect('GET', '/CreditCard/123').respond(status, {}, headers, statusText); + $httpBackend.flush(); + + expect(successCb).toHaveBeenCalledOnce(); + + instance = CreditCard.get({id: 456}, successCb); + headers = {baz: 'qux'}; + status = 299; + statusText = 'KO'; + $httpBackend.expect('GET', '/CreditCard/456').respond(status, {}, headers, statusText); + $httpBackend.flush(); + + expect(successCb).toHaveBeenCalledTimes(2); + }); + }); + describe('failure mode', function() { var ERROR_CODE = 500, ERROR_RESPONSE = 'Server Error', errorCB; beforeEach(function() { - errorCB = jasmine.createSpy('error').andCallFake(function(response) { + errorCB = jasmine.createSpy('error').and.callFake(function(response) { expect(response.data).toBe(ERROR_RESPONSE); expect(response.status).toBe(ERROR_CODE); }); @@ -1102,7 +1684,7 @@ describe("resource", function() { expect(user).toEqualData([{id: 1, name: 'user1'}]); }); - it('should work with the action is overriden', function() { + it('should work with the action is overridden', function() { $httpBackend.expect('GET', '/users.json').respond([{id: 1, name: 'user1'}]); var UserService = $resource('/users/:user_id', {user_id: '@id'}, { query: { @@ -1117,10 +1699,10 @@ describe("resource", function() { }); it('should not convert string literals in array into Resource objects', function() { - $httpBackend.expect('GET', '/names.json').respond(["mary", "jane"]); + $httpBackend.expect('GET', '/names.json').respond(['mary', 'jane']); var strings = $resource('/names.json').query(); $httpBackend.flush(); - expect(strings).toEqualData(["mary", "jane"]); + expect(strings).toEqualData(['mary', 'jane']); }); it('should not convert number literals in array into Resource objects', function() { @@ -1163,7 +1745,7 @@ describe("resource", function() { expect(user).toEqualData({id: 1, name: 'user1'}); }); - it('should work with the action is overriden', function() { + it('should work with the action is overridden', function() { $httpBackend.expect('GET', '/users/1.json').respond({id: 1, name: 'user1'}); var UserService = $resource('/users/:user_id', {user_id: '@id'}, { get: { @@ -1177,7 +1759,7 @@ describe("resource", function() { }); }); - describe("save", function() { + describe('save', function() { it('should append the suffix', function() { $httpBackend.expect('POST', '/users.json', '{"name":"user1"}').respond({id: 123, name: 'user1'}); var UserService = $resource('/users/:user_id.json', {user_id: '@id'}); @@ -1187,8 +1769,8 @@ describe("resource", function() { $httpBackend.flush(); expect(user).toEqualData({id: 123, name: 'user1'}); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0]).toEqual(user); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(user); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); it('should append when an id is supplied', function() { @@ -1199,8 +1781,8 @@ describe("resource", function() { $httpBackend.flush(); expect(user).toEqualData({id: 123, name: 'newName'}); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0]).toEqual(user); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(user); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); it('should append when an id is supplied and the format is a parameter', function() { @@ -1211,8 +1793,8 @@ describe("resource", function() { $httpBackend.flush(); expect(user).toEqualData({id: 123, name: 'newName'}); expect(callback).toHaveBeenCalledOnce(); - expect(callback.mostRecentCall.args[0]).toEqual(user); - expect(callback.mostRecentCall.args[1]()).toEqual({}); + expect(callback.calls.mostRecent().args[0]).toEqual(user); + expect(callback.calls.mostRecent().args[1]()).toEqual(Object.create(null)); }); }); @@ -1229,6 +1811,18 @@ describe("resource", function() { $httpBackend.expect('POST', '/users/.json').respond(); $resource('/users/\\.json').save({}); }); + it('should work with save() if dynamic params', function() { + $httpBackend.expect('POST', '/users/.json').respond(); + $resource('/users/:json', {json: '\\.json'}).save({}); + }); + it('should work with query() if dynamic params', function() { + $httpBackend.expect('GET', '/users/.json').respond(); + $resource('/users/:json', {json: '\\.json'}).query(); + }); + it('should work with get() if dynamic params', function() { + $httpBackend.expect('GET', '/users/.json').respond(); + $resource('/users/:json', {json: '\\.json'}).get(); + }); }); }); @@ -1294,7 +1888,53 @@ describe("resource", function() { }); }); -describe('resource', function() { +describe('extra params', function() { + var $http; + var $httpBackend; + var $resource; + var $rootScope; + + beforeEach(module('ngResource')); + + beforeEach(module(function($provide) { + $provide.decorator('$http', function($delegate) { + return jasmine.createSpy('$http').and.callFake($delegate); + }); + })); + + beforeEach(inject(function(_$http_, _$httpBackend_, _$resource_, _$rootScope_) { + $http = _$http_; + $httpBackend = _$httpBackend_; + $resource = _$resource_; + $rootScope = _$rootScope_; + })); + + afterEach(function() { + $httpBackend.verifyNoOutstandingExpectation(); + }); + + + it('should pass extra params to `$http` as `config.params`', function() { + $httpBackend.expectGET('/bar?baz=qux').respond('{}'); + + var R = $resource('/:foo'); + R.get({foo: 'bar', baz: 'qux'}); + + $rootScope.$digest(); + expect($http).toHaveBeenCalledWith(jasmine.objectContaining({params: {baz: 'qux'}})); + }); + + it('should pass extra params even if `Object.prototype` has properties with the same name', + function() { + $httpBackend.expectGET('/foo?toString=bar').respond('{}'); + + var R = $resource('/foo'); + R.get({toString: 'bar'}); + } + ); +}); + +describe('errors', function() { var $httpBackend, $resource; beforeEach(module(function($exceptionHandlerProvider) { @@ -1321,9 +1961,9 @@ describe('resource', function() { expect(successSpy).not.toHaveBeenCalled(); expect(failureSpy).toHaveBeenCalled(); - expect(failureSpy.mostRecentCall.args[0]).toMatch( - /^\[\$resource:badcfg\] Error in resource configuration for action `query`\. Expected response to contain an array but got an object/ - ); + expect(failureSpy.calls.mostRecent().args[0]).toEqualMinErr('$resource', 'badcfg', + 'Error in resource configuration for action `query`. ' + + 'Expected response to contain an array but got an object (Request: GET /Customer/123)'); }); it('should fail if action expects an array but response is an object', function() { @@ -1338,10 +1978,573 @@ describe('resource', function() { expect(successSpy).not.toHaveBeenCalled(); expect(failureSpy).toHaveBeenCalled(); - expect(failureSpy.mostRecentCall.args[0]).toMatch( - /^\[\$resource:badcfg\] Error in resource configuration for action `get`\. Expected response to contain an object but got an array/ - ); + expect(failureSpy.calls.mostRecent().args[0]).toEqualMinErr('$resource', 'badcfg', + 'Error in resource configuration for action `get`. ' + + 'Expected response to contain an object but got an array (Request: GET /Customer/123)'); + }); +}); + +describe('handling rejections', function() { + var $exceptionHandler; + var $httpBackend; + var $resource; + + beforeEach(module('ngResource')); + beforeEach(module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + })); + + beforeEach(inject(function(_$exceptionHandler_, _$httpBackend_, _$resource_) { + $exceptionHandler = _$exceptionHandler_; + $httpBackend = _$httpBackend_; + $resource = _$resource_; + + $httpBackend.whenGET('/CreditCard').respond(404); + })); + + + it('should reject the promise even when there is an error callback', function() { + var errorCb1 = jasmine.createSpy('errorCb1'); + var errorCb2 = jasmine.createSpy('errorCb2'); + var CreditCard = $resource('/CreditCard'); + + CreditCard.get(noop, errorCb1).$promise.catch(errorCb2); + $httpBackend.flush(); + + expect(errorCb1).toHaveBeenCalledOnce(); + expect(errorCb2).toHaveBeenCalledOnce(); }); + it('should report a PUR when no error callback or responseError interceptor is provided', + function() { + var CreditCard = $resource('/CreditCard'); + + CreditCard.get(); + $httpBackend.flush(); + + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/^Possibly unhandled rejection/); + } + ); + + + it('should not report a PUR when an error callback or responseError interceptor is provided', + function() { + var CreditCard = $resource('/CreditCard', {}, { + test1: { + method: 'GET' + }, + test2: { + method: 'GET', + interceptor: {responseError: function() { return {}; }} + } + }); + + // With error callback + CreditCard.test1(noop, noop); + $httpBackend.flush(); + + expect($exceptionHandler.errors.length).toBe(0); + + // With responseError interceptor + CreditCard.test2(); + $httpBackend.flush(); + + expect($exceptionHandler.errors.length).toBe(0); + + // With error callback and responseError interceptor + CreditCard.test2(noop, noop); + $httpBackend.flush(); + + expect($exceptionHandler.errors.length).toBe(0); + } + ); + + + it('should report a PUR when the responseError interceptor returns a rejected promise', + inject(function($q) { + var CreditCard = $resource('/CreditCard', {}, { + test: { + method: 'GET', + interceptor: {responseError: function() { return $q.reject({}); }} + } + }); + + CreditCard.test(); + $httpBackend.flush(); + + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/^Possibly unhandled rejection/); + }) + ); + + + it('should not swallow exceptions in success callback when error callback is provided', + function() { + $httpBackend.expectGET('/CreditCard/123').respond(null); + var CreditCard = $resource('/CreditCard/:id'); + CreditCard.get({id: 123}, + function(res) { throw new Error('should be caught'); }, + function() {}); + + $httpBackend.flush(); + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/^Error: should be caught/); + } + ); + + + it('should not swallow exceptions in success callback when error callback is not provided', + function() { + $httpBackend.expectGET('/CreditCard/123').respond(null); + var CreditCard = $resource('/CreditCard/:id'); + CreditCard.get({id: 123}, + function(res) { throw new Error('should be caught'); }); + + $httpBackend.flush(); + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/^Error: should be caught/); + } + ); + + + it('should not swallow exceptions in success callback when error callback is provided and has responseError interceptor', + function() { + $httpBackend.expectGET('/CreditCard/123').respond(null); + var CreditCard = $resource('/CreditCard/:id', null, { + get: { + method: 'GET', + interceptor: {responseError: function() {}} + } + }); + + CreditCard.get({id: 123}, + function(res) { throw new Error('should be caught'); }, + function() {}); + + $httpBackend.flush(); + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/^Error: should be caught/); + } + ); + + + it('should not swallow exceptions in success callback when error callback is not provided and has responseError interceptor', + function() { + $httpBackend.expectGET('/CreditCard/123').respond(null); + var CreditCard = $resource('/CreditCard/:id', null, { + get: { + method: 'GET', + interceptor: {responseError: function() {}} + } + }); + + CreditCard.get({id: 123}, + function(res) { throw new Error('should be caught'); }); + + $httpBackend.flush(); + expect($exceptionHandler.errors.length).toBe(1); + expect($exceptionHandler.errors[0]).toMatch(/^Error: should be caught/); + } + ); + + + it('should not propagate exceptions in success callback to the returned promise', function() { + var successCallbackSpy = jasmine.createSpy('successCallback').and.throwError('error'); + var promiseResolveSpy = jasmine.createSpy('promiseResolve'); + var promiseRejectSpy = jasmine.createSpy('promiseReject'); + + $httpBackend.expectGET('/CreditCard/123').respond(null); + var CreditCard = $resource('/CreditCard/:id'); + CreditCard.get({id: 123}, successCallbackSpy). + $promise.then(promiseResolveSpy, promiseRejectSpy); + + $httpBackend.flush(); + expect(successCallbackSpy).toHaveBeenCalled(); + expect(promiseResolveSpy).toHaveBeenCalledWith(jasmine.any(CreditCard)); + expect(promiseRejectSpy).not.toHaveBeenCalled(); + }); + + + it('should not be able to recover from inside the error callback', function() { + var errorCallbackSpy = jasmine.createSpy('errorCallback').and.returnValue({id: 123}); + var promiseResolveSpy = jasmine.createSpy('promiseResolve'); + var promiseRejectSpy = jasmine.createSpy('promiseReject'); + + $httpBackend.expectGET('/CreditCard/123').respond(404); + var CreditCard = $resource('/CreditCard/:id'); + CreditCard.get({id: 123}, noop, errorCallbackSpy). + $promise.then(promiseResolveSpy, promiseRejectSpy); + + $httpBackend.flush(); + expect(errorCallbackSpy).toHaveBeenCalled(); + expect(promiseResolveSpy).not.toHaveBeenCalled(); + expect(promiseRejectSpy).toHaveBeenCalledWith(jasmine.objectContaining({status: 404})); + }); + + + describe('requestInterceptor', function() { + var rejectReason = {'lol':'cat'}; + var $q, $rootScope; + var successSpy, failureSpy, callback; + + beforeEach(inject(function(_$q_, _$rootScope_) { + $q = _$q_; + $rootScope = _$rootScope_; + + successSpy = jasmine.createSpy('successSpy'); + failureSpy = jasmine.createSpy('failureSpy'); + callback = jasmine.createSpy(); + })); + + it('should call requestErrorInterceptor if requestInterceptor throws an error', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function() { + throw rejectReason; + }, + requestError: function(rejection) { + callback(rejection); + return $q.reject(rejection); + } + } + } + }); + + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); + $rootScope.$apply(); + + expect(callback).toHaveBeenCalledOnce(); + expect(callback).toHaveBeenCalledWith(rejectReason); + expect(successSpy).not.toHaveBeenCalled(); + expect(failureSpy).toHaveBeenCalledOnce(); + expect(failureSpy).toHaveBeenCalledWith(rejectReason); + + // Ensure that no requests were made. + $httpBackend.verifyNoOutstandingRequest(); + }); + + it('should abort the operation if a requestErrorInterceptor throws an exception', function() { + var CreditCard = $resource('/CreditCard', {}, { + query: { + method: 'get', + isArray: true, + interceptor: { + request: function() { + return $q.reject(); + }, + requestError: function() { + throw rejectReason; + } + } + } + }); + + var resource = CreditCard.query(); + resource.$promise.then(successSpy, failureSpy); + $rootScope.$apply(); + + expect(resource.$resolved).toBeTruthy(); + expect(successSpy).not.toHaveBeenCalled(); + expect(failureSpy).toHaveBeenCalledOnce(); + expect(failureSpy).toHaveBeenCalledWith(rejectReason); + + // Ensure that no requests were made. + $httpBackend.verifyNoOutstandingRequest(); + }); + }); +}); + +describe('cancelling requests', function() { + var httpSpy; + var $httpBackend; + var $resource; + var $timeout; + + beforeEach(module('ngResource', function($provide) { + $provide.decorator('$http', function($delegate) { + httpSpy = jasmine.createSpy('$http').and.callFake($delegate); + return httpSpy; + }); + })); + + beforeEach(inject(function(_$httpBackend_, _$resource_, _$timeout_) { + $httpBackend = _$httpBackend_; + $resource = _$resource_; + $timeout = _$timeout_; + })); + + it('should accept numeric timeouts in actions and pass them to $http', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + timeout: 10000 + } + }); + + CreditCard.get(); + $httpBackend.flush(); + + expect(httpSpy).toHaveBeenCalledOnce(); + expect(httpSpy.calls.argsFor(0)[0].timeout).toBe(10000); + }); + + it('should delete non-numeric timeouts in actions and log a $debug message', + inject(function($log, $q) { + spyOn($log, 'debug'); + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + timeout: $q.defer().promise + } + }); + + CreditCard.get(); + $httpBackend.flush(); + + expect(httpSpy).toHaveBeenCalledOnce(); + expect(httpSpy.calls.argsFor(0)[0].timeout).toBeUndefined(); + expect($log.debug).toHaveBeenCalledOnceWith('ngResource:\n' + + ' Only numeric values are allowed as `timeout`.\n' + + ' Promises are not supported in $resource, because the same value would ' + + 'be used for multiple requests. If you are looking for a way to cancel ' + + 'requests, you should use the `cancellable` option.'); + }) + ); + + it('should use `cancellable` value if passed a non-numeric `timeout` in an action', + inject(function($log, $q, $rootScope) { + spyOn($log, 'debug'); + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + timeout: $q.defer().promise, + cancellable: true + } + }); + + var creditCard = CreditCard.get(); + $rootScope.$digest(); + expect(creditCard.$cancelRequest).toBeDefined(); + expect(httpSpy.calls.argsFor(0)[0].timeout).toEqual(jasmine.any($q)); + expect(httpSpy.calls.argsFor(0)[0].timeout.then).toBeDefined(); + + expect($log.debug).toHaveBeenCalledOnceWith('ngResource:\n' + + ' Only numeric values are allowed as `timeout`.\n' + + ' Promises are not supported in $resource, because the same value would ' + + 'be used for multiple requests. If you are looking for a way to cancel ' + + 'requests, you should use the `cancellable` option.'); + }) + ); + + it('should not create a `$cancelRequest` method for instance calls', function() { + $httpBackend.whenPOST('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + save1: { + method: 'POST', + cancellable: false + }, + save2: { + method: 'POST', + cancellable: true + } + }); + + var creditCard = new CreditCard(); + + var promise1 = creditCard.$save1(); + expect(promise1.$cancelRequest).toBeUndefined(); + expect(creditCard.$cancelRequest).toBeUndefined(); + + var promise2 = creditCard.$save2(); + expect(promise2.$cancelRequest).toBeUndefined(); + expect(creditCard.$cancelRequest).toBeUndefined(); + + $httpBackend.flush(); + expect(promise1.$cancelRequest).toBeUndefined(); + expect(promise2.$cancelRequest).toBeUndefined(); + expect(creditCard.$cancelRequest).toBeUndefined(); + }); + + it('should not create a `$cancelRequest` method for non-cancellable calls', function() { + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + cancellable: false + } + }); + + var creditCard = CreditCard.get(); + + expect(creditCard.$cancelRequest).toBeUndefined(); + }); + + it('should also take into account `options.cancellable`', function() { + var options = {cancellable: true}; + var CreditCard = $resource('/CreditCard', {}, { + get1: {method: 'GET', cancellable: false}, + get2: {method: 'GET', cancellable: true}, + get3: {method: 'GET'} + }, options); + + var creditCard1 = CreditCard.get1(); + var creditCard2 = CreditCard.get2(); + var creditCard3 = CreditCard.get3(); + + expect(creditCard1.$cancelRequest).toBeUndefined(); + expect(creditCard2.$cancelRequest).toBeDefined(); + expect(creditCard3.$cancelRequest).toBeDefined(); + + options = {cancellable: false}; + CreditCard = $resource('/CreditCard', {}, { + get1: {method: 'GET', cancellable: false}, + get2: {method: 'GET', cancellable: true}, + get3: {method: 'GET'} + }, options); + + creditCard1 = CreditCard.get1(); + creditCard2 = CreditCard.get2(); + creditCard3 = CreditCard.get3(); + + expect(creditCard1.$cancelRequest).toBeUndefined(); + expect(creditCard2.$cancelRequest).toBeDefined(); + expect(creditCard3.$cancelRequest).toBeUndefined(); + }); + + it('should accept numeric timeouts in cancellable actions and cancel the request when timeout occurs', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + timeout: 10000, + cancellable: true + } + }); + + var ccs = CreditCard.get(); + ccs.$promise.catch(noop); + $timeout.flush(); + expect($httpBackend.flush).toThrow(new Error('No pending request to flush !')); + + CreditCard.get(); + expect($httpBackend.flush).not.toThrow(); + + }); + + it('should cancel the request (if cancellable), when calling `$cancelRequest`', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + cancellable: true + } + }); + + var ccs = CreditCard.get(); + ccs.$cancelRequest(); + expect($httpBackend.flush).toThrow(new Error('No pending request to flush !')); + + CreditCard.get(); + expect($httpBackend.flush).not.toThrow(); + }); + + it('should cancel the request, when calling `$cancelRequest` in cancellable actions with timeout defined', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + timeout: 10000, + cancellable: true + } + }); + + var ccs = CreditCard.get(); + ccs.$cancelRequest(); + expect($httpBackend.flush).toThrow(new Error('No pending request to flush !')); + + CreditCard.get(); + expect($httpBackend.flush).not.toThrow(); + }); + + it('should reset `$cancelRequest` after the response arrives', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + cancellable: true + } + }); + + var creditCard = CreditCard.get(); + + expect(creditCard.$cancelRequest).not.toBe(noop); + + $httpBackend.flush(); + + expect(creditCard.$cancelRequest).toBe(noop); + }); + + it('should not break when calling old `$cancelRequest` after the response arrives', function() { + $httpBackend.whenGET('/CreditCard').respond({}); + + var CreditCard = $resource('/CreditCard', {}, { + get: { + method: 'GET', + cancellable: true + } + }); + + var creditCard = CreditCard.get(); + var cancelRequest = creditCard.$cancelRequest; + + $httpBackend.flush(); + + expect(cancelRequest).not.toBe(noop); + expect(cancelRequest).not.toThrow(); + }); +}); + +describe('configuring `cancellable` on the provider', function() { + var $resource; + + beforeEach(module('ngResource', function($resourceProvider) { + $resourceProvider.defaults.cancellable = true; + })); + + beforeEach(inject(function(_$resource_) { + $resource = _$resource_; + })); + + it('should also take into account `$resourceProvider.defaults.cancellable`', function() { + var CreditCard = $resource('/CreditCard', {}, { + get1: {method: 'GET', cancellable: false}, + get2: {method: 'GET', cancellable: true}, + get3: {method: 'GET'} + }); + + var creditCard1 = CreditCard.get1(); + var creditCard2 = CreditCard.get2(); + var creditCard3 = CreditCard.get3(); + + expect(creditCard1.$cancelRequest).toBeUndefined(); + expect(creditCard2.$cancelRequest).toBeDefined(); + expect(creditCard3.$cancelRequest).toBeDefined(); + }); +}); }); diff --git a/test/ngRoute/directive/ngViewSpec.js b/test/ngRoute/directive/ngViewSpec.js index c910ba509b0d..83f9b3c12c9a 100644 --- a/test/ngRoute/directive/ngViewSpec.js +++ b/test/ngRoute/directive/ngViewSpec.js @@ -1,983 +1,1098 @@ 'use strict'; describe('ngView', function() { - var element; - beforeEach(module('ngRoute')); + describe('basics', function() { + var element; - beforeEach(module(function($provide) { - return function($rootScope, $compile, $animate) { - element = $compile('
          ')($rootScope); - }; - })); + beforeEach(module('ngRoute')); + beforeEach(module(function($provide) { + return function($rootScope, $compile, $animate) { + element = $compile('
          ')($rootScope); + }; + })); - afterEach(function() { - dealoc(element); - }); + + afterEach(function() { + dealoc(element); + }); - it('should do nothing when no routes are defined', - inject(function($rootScope, $compile, $location) { - $location.path('/unknown'); - $rootScope.$digest(); - expect(element.text()).toEqual(''); - })); + it('should do nothing when no routes are defined', + inject(function($rootScope, $compile, $location) { + $location.path('/unknown'); + $rootScope.$digest(); + expect(element.text()).toEqual(''); + })); - it('should instantiate controller after compiling the content', function() { - var log = [], controllerScope, - Ctrl = function($scope) { - controllerScope = $scope; - log.push('ctrl-init'); - }; + it('should instantiate controller after compiling the content', function() { + var log = [], controllerScope, + Ctrl = function($scope) { + controllerScope = $scope; + log.push('ctrl-init'); + }; - module(function($compileProvider, $routeProvider) { - $compileProvider.directive('compileLog', function() { - return { - compile: function() { - log.push('compile'); - } - }; + module(function($compileProvider, $routeProvider) { + $compileProvider.directive('compileLog', function() { + return { + compile: function() { + log.push('compile'); + } + }; + }); + + $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl}); }); - $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl}); + inject(function($route, $rootScope, $templateCache, $location) { + $templateCache.put('/tpl.html', [200, '
          partial
          ', {}]); + $location.path('/some'); + $rootScope.$digest(); + + expect(controllerScope.$parent).toBe($rootScope); + expect(controllerScope).toBe($route.current.scope); + expect(log).toEqual(['compile', 'ctrl-init']); + }); }); - inject(function($route, $rootScope, $templateCache, $location) { - $templateCache.put('/tpl.html', [200, '
          partial
          ', {}]); - $location.path('/some'); - $rootScope.$digest(); - expect(controllerScope.$parent).toBe($rootScope); - expect(controllerScope).toBe($route.current.scope); - expect(log).toEqual(['compile', 'ctrl-init']); - }); - }); + it('should instantiate the associated controller when an empty template is downloaded', function() { + var log = [], controllerScope, + Ctrl = function($scope) { + controllerScope = $scope; + log.push('ctrl-init'); + }; + + module(function($routeProvider) { + $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl}); + }); + inject(function($route, $rootScope, $templateCache, $location) { + $templateCache.put('/tpl.html', [200, '', {}]); + $location.path('/some'); - it('should instantiate the associated controller when an empty template is downloaded', function() { - var log = [], controllerScope, - Ctrl = function($scope) { - controllerScope = $scope; - log.push('ctrl-init'); - }; + expect(function() { + $rootScope.$digest(); + }).not.toThrow(); - module(function($routeProvider) { - $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl}); + expect(controllerScope).toBeDefined(); + }); }); - inject(function($route, $rootScope, $templateCache, $location) { - $templateCache.put('/tpl.html', [200, '', {}]); - $location.path('/some'); - expect(function() { + it('should instantiate controller with an alias', function() { + var log = [], controllerScope; + + function Ctrl($scope) { + this.name = 'alias'; + controllerScope = $scope; + } + + module(function($compileProvider, $routeProvider) { + $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl, controllerAs: 'ctrl'}); + }); + + inject(function($route, $rootScope, $templateCache, $location) { + $templateCache.put('/tpl.html', [200, '
          ', {}]); + $location.path('/some'); $rootScope.$digest(); - }).not.toThrow(); - expect(controllerScope).toBeDefined(); + expect(controllerScope.ctrl.name).toBe('alias'); + }); }); - }); - it('should instantiate controller with an alias', function() { - var log = [], controllerScope, - Ctrl = function($scope) { - this.name = 'alias'; - controllerScope = $scope; - }; + it('should support string controller declaration', function() { + var MyCtrl = jasmine.createSpy('MyCtrl'); - module(function($compileProvider, $routeProvider) { - $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl, controllerAs: 'ctrl'}); - }); + module(function($controllerProvider, $routeProvider) { + $controllerProvider.register('MyCtrl', ['$scope', MyCtrl]); + $routeProvider.when('/foo', {controller: 'MyCtrl', templateUrl: '/tpl.html'}); + }); - inject(function($route, $rootScope, $templateCache, $location) { - $templateCache.put('/tpl.html', [200, '
          ', {}]); - $location.path('/some'); - $rootScope.$digest(); + inject(function($route, $location, $rootScope, $templateCache) { + $templateCache.put('/tpl.html', [200, '
          ', {}]); + $location.path('/foo'); + $rootScope.$digest(); - expect(controllerScope.ctrl.name).toBe('alias'); + expect($route.current.controller).toBe('MyCtrl'); + expect(MyCtrl).toHaveBeenCalledWith(element.children().scope()); + }); }); - }); - it('should support string controller declaration', function() { - var MyCtrl = jasmine.createSpy('MyCtrl'); + it('should reference resolved locals in scope', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', { + resolve: { + name: function() { + return 'shahar'; + } + }, + template: '
          {{$resolve.name}}
          ' + }); + }); - module(function($controllerProvider, $routeProvider) { - $controllerProvider.register('MyCtrl', ['$scope', MyCtrl]); - $routeProvider.when('/foo', {controller: 'MyCtrl', templateUrl: '/tpl.html'}); + inject(function($location, $rootScope) { + $location.path('/foo'); + $rootScope.$digest(); + expect(element.text()).toEqual('shahar'); + }); }); - inject(function($route, $location, $rootScope, $templateCache) { - $templateCache.put('/tpl.html', [200, '
          ', {}]); - $location.path('/foo'); - $rootScope.$digest(); - expect($route.current.controller).toBe('MyCtrl'); - expect(MyCtrl).toHaveBeenCalledWith(element.children().scope()); + it('should allow to provide an alias for resolved locals using resolveAs', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', { + resolveAs: 'myResolve', + resolve: { + name: function() { + return 'shahar'; + } + }, + template: '
          {{myResolve.name}}
          ' + }); + }); + + inject(function($location, $rootScope) { + $location.path('/foo'); + $rootScope.$digest(); + expect(element.text()).toEqual('shahar'); + }); }); - }); - it('should load content via xhr when route changes', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); - $routeProvider.when('/bar', {templateUrl: 'myUrl2'}); - }); + it('should load content via xhr when route changes', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); + $routeProvider.when('/bar', {templateUrl: 'myUrl2'}); + }); - inject(function($rootScope, $compile, $httpBackend, $location, $route) { - expect(element.text()).toEqual(''); + inject(function($rootScope, $compile, $httpBackend, $location, $route) { + expect(element.text()).toEqual(''); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
          {{1+3}}
          '); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('4'); + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
          {{1+3}}
          '); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('4'); - $location.path('/bar'); - $httpBackend.expect('GET', 'myUrl2').respond('angular is da best'); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('angular is da best'); + $location.path('/bar'); + $httpBackend.expect('GET', 'myUrl2').respond('angular is da best'); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('angular is da best'); + }); }); - }); - it('should use inline content route changes', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {template: '
          {{1+3}}
          '}); - $routeProvider.when('/bar', {template: 'angular is da best'}); - $routeProvider.when('/blank', {template: ''}); - }); + it('should use inline content route changes', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {template: '
          {{1+3}}
          '}); + $routeProvider.when('/bar', {template: 'AngularJS is da best'}); + $routeProvider.when('/blank', {template: ''}); + }); - inject(function($rootScope, $compile, $location, $route) { - expect(element.text()).toEqual(''); + inject(function($rootScope, $compile, $location, $route) { + expect(element.text()).toEqual(''); - $location.path('/foo'); - $rootScope.$digest(); - expect(element.text()).toEqual('4'); + $location.path('/foo'); + $rootScope.$digest(); + expect(element.text()).toEqual('4'); - $location.path('/bar'); - $rootScope.$digest(); - expect(element.text()).toEqual('angular is da best'); + $location.path('/bar'); + $rootScope.$digest(); + expect(element.text()).toEqual('AngularJS is da best'); - $location.path('/blank'); - $rootScope.$digest(); - expect(element.text()).toEqual(''); + $location.path('/blank'); + $rootScope.$digest(); + expect(element.text()).toEqual(''); + }); }); - }); - it('should remove all content when location changes to an unknown route', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); - }); + it('should remove all content when location changes to an unknown route', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); + }); - inject(function($rootScope, $compile, $location, $httpBackend, $route) { - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
          {{1+3}}
          '); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('4'); + inject(function($rootScope, $compile, $location, $httpBackend, $route) { + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
          {{1+3}}
          '); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('4'); - $location.path('/unknown'); - $rootScope.$digest(); - expect(element.text()).toEqual(''); + $location.path('/unknown'); + $rootScope.$digest(); + expect(element.text()).toEqual(''); + }); }); - }); - it('should chain scopes and propagate evals to the child scope', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); - }); + it('should chain scopes and propagate evals to the child scope', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); + }); - inject(function($rootScope, $compile, $location, $httpBackend, $route) { - $rootScope.parentVar = 'parent'; + inject(function($rootScope, $compile, $location, $httpBackend, $route) { + $rootScope.parentVar = 'parent'; - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
          {{parentVar}}
          '); - $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toEqual('parent'); + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
          {{parentVar}}
          '); + $rootScope.$digest(); + $httpBackend.flush(); + expect(element.text()).toEqual('parent'); - $rootScope.parentVar = 'new parent'; - $rootScope.$digest(); - expect(element.text()).toEqual('new parent'); + $rootScope.parentVar = 'new parent'; + $rootScope.$digest(); + expect(element.text()).toEqual('new parent'); + }); }); - }); - it('should be possible to nest ngView in ngInclude', function() { + it('should be possible to nest ngView in ngInclude', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'viewPartial.html'}); - }); + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'viewPartial.html'}); + }); - inject(function($httpBackend, $location, $route, $compile, $rootScope) { - $httpBackend.whenGET('includePartial.html').respond('view: '); - $httpBackend.whenGET('viewPartial.html').respond('content'); - $location.path('/foo'); + inject(function($httpBackend, $location, $route, $compile, $rootScope) { + $httpBackend.whenGET('includePartial.html').respond('view: '); + $httpBackend.whenGET('viewPartial.html').respond('content'); + $location.path('/foo'); - var elm = $compile( - '
          ' + - 'include: ' + - '
          ')($rootScope); - $rootScope.$digest(); - $httpBackend.flush(); + var elm = $compile( + '
          ' + + 'include: ' + + '
          ')($rootScope); + $rootScope.$digest(); + $httpBackend.flush(); - expect(elm.text()).toEqual('include: view: content'); - expect($route.current.templateUrl).toEqual('viewPartial.html'); - dealoc(elm); + expect(elm.text()).toEqual('include: view: content'); + expect($route.current.templateUrl).toEqual('viewPartial.html'); + dealoc(elm); + }); }); - }); - it('should initialize view template after the view controller was initialized even when ' + - 'templates were cached', function() { - //this is a test for a regression that was introduced by making the ng-view cache sync - function ParentCtrl($scope) { - $scope.log.push('parent'); - } + it('should initialize view template after the view controller was initialized even when ' + + 'templates were cached', function() { + //this is a test for a regression that was introduced by making the ng-view cache sync + function ParentCtrl($scope) { + $scope.log.push('parent'); + } - module(function($routeProvider) { - $routeProvider.when('/foo', {controller: ParentCtrl, templateUrl: 'viewPartial.html'}); - }); + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: ParentCtrl, templateUrl: 'viewPartial.html'}); + }); - inject(function($rootScope, $compile, $location, $httpBackend, $route) { - $rootScope.log = []; + inject(function($rootScope, $compile, $location, $httpBackend, $route) { + $rootScope.log = []; - $rootScope.ChildCtrl = function($scope) { - $scope.log.push('child'); - }; + $rootScope.ChildCtrl = function($scope) { + $scope.log.push('child'); + }; - $location.path('/foo'); - $httpBackend.expect('GET', 'viewPartial.html'). - respond('
          ' + - '
          ' + - '
          '); - $rootScope.$apply(); - $httpBackend.flush(); + $location.path('/foo'); + $httpBackend.expect('GET', 'viewPartial.html'). + respond('
          ' + + '
          ' + + '
          '); + $rootScope.$apply(); + $httpBackend.flush(); - expect($rootScope.log).toEqual(['parent', 'init', 'child']); + expect($rootScope.log).toEqual(['parent', 'init', 'child']); - $location.path('/'); - $rootScope.$apply(); - expect($rootScope.log).toEqual(['parent', 'init', 'child']); + $location.path('/'); + $rootScope.$apply(); + expect($rootScope.log).toEqual(['parent', 'init', 'child']); - $rootScope.log = []; - $location.path('/foo'); - $rootScope.$apply(); + $rootScope.log = []; + $location.path('/foo'); + $rootScope.$apply(); - expect($rootScope.log).toEqual(['parent', 'init', 'child']); + expect($rootScope.log).toEqual(['parent', 'init', 'child']); + }); }); - }); - it('should discard pending xhr callbacks if a new route is requested before the current ' + - 'finished loading', function() { - // this is a test for a bad race condition that affected feedback + it('should discard pending xhr callbacks if a new route is requested before the current ' + + 'finished loading', function() { + // this is a test for a bad race condition that affected feedback - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); - $routeProvider.when('/bar', {templateUrl: 'myUrl2'}); - }); + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); + $routeProvider.when('/bar', {templateUrl: 'myUrl2'}); + }); - inject(function($route, $rootScope, $location, $httpBackend) { - expect(element.text()).toEqual(''); + inject(function($route, $rootScope, $location, $httpBackend) { + expect(element.text()).toEqual(''); - $location.path('/foo'); - $httpBackend.expect('GET', 'myUrl1').respond('
          {{1+3}}
          '); - $rootScope.$digest(); - $location.path('/bar'); - $httpBackend.expect('GET', 'myUrl2').respond('
          {{1+1}}
          '); - $rootScope.$digest(); - $httpBackend.flush(); // now that we have two requests pending, flush! + $location.path('/foo'); + $httpBackend.expect('GET', 'myUrl1').respond('
          {{1+3}}
          '); + $rootScope.$digest(); + $location.path('/bar'); + $httpBackend.expect('GET', 'myUrl2').respond('
          {{1+1}}
          '); + $rootScope.$digest(); + $httpBackend.flush(); // now that we have two requests pending, flush! - expect(element.text()).toEqual('2'); + expect(element.text()).toEqual('2'); + }); }); - }); - it('should be async even if served from cache', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {controller: angular.noop, templateUrl: 'myUrl1'}); - }); + it('should be async even if served from cache', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: angular.noop, templateUrl: 'myUrl1'}); + }); - inject(function($route, $rootScope, $location, $templateCache) { - $templateCache.put('myUrl1', [200, 'my partial', {}]); - $location.path('/foo'); + inject(function($route, $rootScope, $location, $templateCache) { + $templateCache.put('myUrl1', [200, 'my partial', {}]); + $location.path('/foo'); - var called = 0; - // we want to assert only during first watch - $rootScope.$watch(function() { - if (!called) expect(element.text()).toBe(''); - called++; - }); + var called = 0; + // we want to assert only during first watch + $rootScope.$watch(function() { + if (!called) expect(element.text()).toBe(''); + called++; + }); - $rootScope.$digest(); - expect(element.text()).toBe('my partial'); + $rootScope.$digest(); + expect(element.text()).toBe('my partial'); + }); }); - }); - it('should fire $contentLoaded event when content compiled and linked', function() { - var log = []; - var logger = function(name) { - return function() { - log.push(name); + it('should fire $contentLoaded event when content compiled and linked', function() { + var log = []; + var logger = function(name) { + return function() { + log.push(name); + }; + }; + var Ctrl = function($scope) { + $scope.value = 'bound-value'; + log.push('init-ctrl'); }; - }; - var Ctrl = function($scope) { - $scope.value = 'bound-value'; - log.push('init-ctrl'); - }; - - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: Ctrl}); - }); - inject(function($templateCache, $rootScope, $location) { - $rootScope.$on('$routeChangeStart', logger('$routeChangeStart')); - $rootScope.$on('$routeChangeSuccess', logger('$routeChangeSuccess')); - $rootScope.$on('$viewContentLoaded', logger('$viewContentLoaded')); + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: Ctrl}); + }); - $templateCache.put('tpl.html', [200, '{{value}}', {}]); - $location.path('/foo'); - $rootScope.$digest(); + inject(function($templateCache, $rootScope, $location) { + $rootScope.$on('$routeChangeStart', logger('$routeChangeStart')); + $rootScope.$on('$routeChangeSuccess', logger('$routeChangeSuccess')); + $rootScope.$on('$viewContentLoaded', logger('$viewContentLoaded')); - expect(element.text()).toBe('bound-value'); - expect(log).toEqual([ - '$routeChangeStart', 'init-ctrl', '$viewContentLoaded', '$routeChangeSuccess' - ]); - }); - }); + $templateCache.put('tpl.html', [200, '{{value}}', {}]); + $location.path('/foo'); + $rootScope.$digest(); - it('should destroy previous scope', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'tpl.html'}); + expect(element.text()).toBe('bound-value'); + expect(log).toEqual([ + '$routeChangeStart', 'init-ctrl', '$viewContentLoaded', '$routeChangeSuccess' + ]); + }); }); - inject(function($templateCache, $rootScope, $location) { - $templateCache.put('tpl.html', [200, 'partial', {}]); + it('should destroy previous scope', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'tpl.html'}); + }); - expect($rootScope.$$childHead).toBeNull(); - expect($rootScope.$$childTail).toBeNull(); + inject(function($templateCache, $rootScope, $location) { + $templateCache.put('tpl.html', [200, 'partial', {}]); - $location.path('/foo'); - $rootScope.$digest(); + expect($rootScope.$$childHead).toBeNull(); + expect($rootScope.$$childTail).toBeNull(); - expect(element.text()).toBe('partial'); - expect($rootScope.$$childHead).not.toBeNull(); - expect($rootScope.$$childTail).not.toBeNull(); + $location.path('/foo'); + $rootScope.$digest(); - $location.path('/non/existing/route'); - $rootScope.$digest(); + expect(element.text()).toBe('partial'); + expect($rootScope.$$childHead).not.toBeNull(); + expect($rootScope.$$childTail).not.toBeNull(); - expect(element.text()).toBe(''); - expect($rootScope.$$childHead).toBeNull(); - expect($rootScope.$$childTail).toBeNull(); + $location.path('/non/existing/route'); + $rootScope.$digest(); + + expect(element.text()).toBe(''); + expect($rootScope.$$childHead).toBeNull(); + expect($rootScope.$$childTail).toBeNull(); + }); }); - }); - it('should destroy previous scope if multiple route changes occur before server responds', - function() { - var log = []; - var createCtrl = function(name) { - return function($scope) { - log.push('init-' + name); - $scope.$on('$destroy', function() {log.push('destroy-' + name);}); + it('should destroy previous scope if multiple route changes occur before server responds', + function() { + var log = []; + var createCtrl = function(name) { + return function($scope) { + log.push('init-' + name); + $scope.$on('$destroy', function() {log.push('destroy-' + name);}); + }; }; - }; - module(function($routeProvider) { - $routeProvider.when('/one', {templateUrl: 'one.html', controller: createCtrl('ctrl1')}); - $routeProvider.when('/two', {templateUrl: 'two.html', controller: createCtrl('ctrl2')}); - }); + module(function($routeProvider) { + $routeProvider.when('/one', {templateUrl: 'one.html', controller: createCtrl('ctrl1')}); + $routeProvider.when('/two', {templateUrl: 'two.html', controller: createCtrl('ctrl2')}); + }); - inject(function($httpBackend, $rootScope, $location) { - $httpBackend.whenGET('one.html').respond('content 1'); - $httpBackend.whenGET('two.html').respond('content 2'); + inject(function($httpBackend, $rootScope, $location) { + $httpBackend.whenGET('one.html').respond('content 1'); + $httpBackend.whenGET('two.html').respond('content 2'); - $location.path('/one'); - $rootScope.$digest(); - $location.path('/two'); - $rootScope.$digest(); + $location.path('/one'); + $rootScope.$digest(); + $location.path('/two'); + $rootScope.$digest(); - $httpBackend.flush(); - expect(element.text()).toBe('content 2'); - expect(log).toEqual(['init-ctrl2']); + $httpBackend.flush(); + expect(element.text()).toBe('content 2'); + expect(log).toEqual(['init-ctrl2']); - $location.path('/non-existing'); - $rootScope.$digest(); + $location.path('/non-existing'); + $rootScope.$digest(); - expect(element.text()).toBe(''); - expect(log).toEqual(['init-ctrl2', 'destroy-ctrl2']); + expect(element.text()).toBe(''); + expect(log).toEqual(['init-ctrl2', 'destroy-ctrl2']); - expect($rootScope.$$childHead).toBeNull(); - expect($rootScope.$$childTail).toBeNull(); + expect($rootScope.$$childHead).toBeNull(); + expect($rootScope.$$childTail).toBeNull(); + }); }); - }); - it('should $destroy scope after update and reload', function() { - // this is a regression of bug, where $route doesn't copy scope when only updating + it('should $destroy scope after update and reload', function() { + // this is a regression of bug, where $route doesn't copy scope when only updating - var log = []; + var log = []; - function logger(msg) { - return function() { - log.push(msg); - }; - } + function logger(msg) { + return function() { + log.push(msg); + }; + } - function createController(name) { - return function($scope) { - log.push('init-' + name); - $scope.$on('$destroy', logger('destroy-' + name)); - $scope.$on('$routeUpdate', logger('route-update')); - }; - } + function createController(name) { + return function($scope) { + log.push('init-' + name); + $scope.$on('$destroy', logger('destroy-' + name)); + $scope.$on('$routeUpdate', logger('route-update')); + }; + } - module(function($routeProvider) { - $routeProvider.when('/bar', {templateUrl: 'tpl.html', controller: createController('bar')}); - $routeProvider.when('/foo', { - templateUrl: 'tpl.html', - controller: createController('foo'), - reloadOnSearch: false + module(function($routeProvider) { + $routeProvider.when('/bar', {templateUrl: 'tpl.html', controller: createController('bar')}); + $routeProvider.when('/foo', { + templateUrl: 'tpl.html', + controller: createController('foo'), + reloadOnSearch: false + }); }); - }); - inject(function($templateCache, $location, $rootScope) { - $templateCache.put('tpl.html', [200, 'partial', {}]); + inject(function($templateCache, $location, $rootScope) { + $templateCache.put('tpl.html', [200, 'partial', {}]); - $location.url('/foo'); - $rootScope.$digest(); - expect(log).toEqual(['init-foo']); + $location.url('/foo'); + $rootScope.$digest(); + expect(log).toEqual(['init-foo']); - $location.search({q: 'some'}); - $rootScope.$digest(); - expect(log).toEqual(['init-foo', 'route-update']); + $location.search({q: 'some'}); + $rootScope.$digest(); + expect(log).toEqual(['init-foo', 'route-update']); - $location.url('/bar'); - $rootScope.$digest(); - expect(log).toEqual(['init-foo', 'route-update', 'destroy-foo', 'init-bar']); + $location.url('/bar'); + $rootScope.$digest(); + expect(log).toEqual(['init-foo', 'route-update', 'destroy-foo', 'init-bar']); + }); }); - }); - it('should evaluate onload expression after linking the content', function() { - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'tpl.html'}); - }); + it('should evaluate onload expression after linking the content', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'tpl.html'}); + }); - inject(function($templateCache, $location, $rootScope) { - $templateCache.put('tpl.html', [200, '{{1+1}}', {}]); - $rootScope.load = jasmine.createSpy('onload'); + inject(function($templateCache, $location, $rootScope) { + $templateCache.put('tpl.html', [200, '{{1+1}}', {}]); + $rootScope.load = jasmine.createSpy('onload'); - $location.url('/foo'); - $rootScope.$digest(); - expect($rootScope.load).toHaveBeenCalledOnce(); + $location.url('/foo'); + $rootScope.$digest(); + expect($rootScope.load).toHaveBeenCalledOnce(); + }); }); - }); - it('should set $scope and $controllerController on the view elements (except for non-element nodes)', function() { - function MyCtrl($scope) { - $scope.state = 'WORKS'; - $scope.ctrl = this; - } + it('should set $scope and $controllerController on the view elements (except for non-element nodes)', function() { + function MyCtrl($scope) { + $scope.state = 'WORKS'; + $scope.ctrl = this; + } - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: MyCtrl}); - }); + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: MyCtrl}); + }); - inject(function($templateCache, $location, $rootScope, $route) { - // in the template the white-space before the div is an intentional non-element node, - // a text might get wrapped into span so it's safer to just use white space - $templateCache.put('tpl.html', [200, ' \n
          {{state}}
          ', {}]); + inject(function($templateCache, $location, $rootScope, $route) { + // in the template the white-space before the div is an intentional non-element node, + // a text might get wrapped into span so it's safer to just use white space + $templateCache.put('tpl.html', [200, ' \n
          {{state}}
          ', {}]); - $location.url('/foo'); - $rootScope.$digest(); - // using toMatch because in IE8+jquery the space doesn't get preserved. jquery bug? - expect(element.text()).toMatch(/\s*WORKS/); + $location.url('/foo'); + $rootScope.$digest(); + expect(element.text()).toEqual(' \n WORKS'); - var div = element.find('div'); - expect(div.parent()[0].nodeName.toUpperCase()).toBeOneOf('NG:VIEW', 'VIEW'); + var div = element.find('div'); + expect(div.parent()[0].nodeName.toUpperCase()).toBeOneOf('NG:VIEW', 'VIEW'); - expect(div.scope()).toBe($route.current.scope); - expect(div.scope().hasOwnProperty('state')).toBe(true); - expect(div.scope().state).toEqual('WORKS'); + expect(div.scope()).toBe($route.current.scope); + expect(div.scope().hasOwnProperty('state')).toBe(true); + expect(div.scope().state).toEqual('WORKS'); - expect(div.controller()).toBe($route.current.scope.ctrl); + expect(div.controller()).toBe($route.current.scope.ctrl); + }); }); - }); - it('should not set $scope or $controllerController on top level text elements in the view', function() { - function MyCtrl($scope) {} + it('should not set $scope or $controllerController on top level text elements in the view', function() { + function MyCtrl($scope) {} - module(function($routeProvider) { - $routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: MyCtrl}); - }); + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'tpl.html', controller: MyCtrl}); + }); - inject(function($templateCache, $location, $rootScope, $route) { - $templateCache.put('tpl.html', '
          '); - $location.url('/foo'); - $rootScope.$digest(); + inject(function($templateCache, $location, $rootScope, $route) { + $templateCache.put('tpl.html', '
          '); + $location.url('/foo'); + $rootScope.$digest(); - angular.forEach(element.contents(), function(node) { - if (node.nodeType == 3 /* text node */) { - expect(angular.element(node).scope()).not.toBe($route.current.scope); - expect(angular.element(node).controller()).not.toBeDefined(); - } else if (node.nodeType == 8 /* comment node */) { - expect(angular.element(node).scope()).toBe(element.scope()); - expect(angular.element(node).controller()).toBe(element.controller()); - } else { - expect(angular.element(node).scope()).toBe($route.current.scope); - expect(angular.element(node).controller()).toBeDefined(); - } + angular.forEach(element.contents(), function(node) { + if (node.nodeType === 3 /* text node */) { + expect(angular.element(node).scope()).not.toBe($route.current.scope); + expect(angular.element(node).controller()).not.toBeDefined(); + } else if (node.nodeType === 8 /* comment node */) { + expect(angular.element(node).scope()).toBe(element.scope()); + expect(angular.element(node).controller()).toBe(element.controller()); + } else { + expect(angular.element(node).scope()).toBe($route.current.scope); + expect(angular.element(node).controller()).toBeDefined(); + } + }); }); }); - }); -}); -describe('ngView and transcludes', function() { - var element, directive; - beforeEach(module('ngRoute', function($compileProvider) { - element = null; - directive = $compileProvider.directive; - })); + it('should not trigger a digest when the view is changed', function() { + module(function($routeProvider) { + $routeProvider.when('/foo', {templateUrl: 'myUrl1'}); + $routeProvider.when('/bar', {templateUrl: 'myUrl2'}); + }); - afterEach(function() { - if (element) { - dealoc(element); - } - }); + inject(function($$rAF, $templateCache, $rootScope, $compile, $timeout, $location, $httpBackend) { + var spy = spyOn($rootScope, '$digest').and.callThrough(); - it('should allow access to directive controller from children when used in a replace template', function() { - var controller; - module(function($routeProvider) { - $routeProvider.when('/view', {templateUrl: 'view.html'}); - directive('template', function() { - return { - template: '
          ', - replace: true, - controller: function() { - this.flag = true; - } - }; - }); + $templateCache.put('myUrl1', 'my template content'); + $templateCache.put('myUrl2', 'my other template content'); - directive('test', function() { - return { - require: '^template', - link: function(scope, el, attr, ctrl) { - controller = ctrl; - } - }; + $location.path('/foo'); + $rootScope.$digest(); + + // The animation completion is async even without actual animations + $$rAF.flush(); + expect(element.text()).toEqual('my template content'); + + $location.path('/bar'); + $rootScope.$digest(); + spy.calls.reset(); + + $$rAF.flush(); + expect(element.text()).toEqual('my other template content'); + + expect(spy).not.toHaveBeenCalled(); + // A digest may have been triggered asynchronously, so check the queue + $timeout.verifyNoPendingTasks(); }); }); - inject(function($compile, $rootScope, $httpBackend, $location) { - $httpBackend.expectGET('view.html').respond('
          '); - element = $compile('
          ')($rootScope); - $location.url('/view'); - $rootScope.$apply(); - $httpBackend.flush(); - expect(controller.flag).toBe(true); - }); + }); - it("should compile its content correctly (although we remove it later)", function() { - var testElement; - module(function($compileProvider, $routeProvider) { - $routeProvider.when('/view', {template: ' '}); - var directive = $compileProvider.directive; - directive('test', function() { - return { - link: function(scope, element) { - testElement = element; - } - }; - }); - }); - inject(function($compile, $rootScope, $location) { - element = $compile('
          ')($rootScope); - $location.url('/view'); - $rootScope.$apply(); - expect(testElement[0].nodeName).toBe('DIV'); + describe('and transcludes', function() { + var element, directive; + + beforeEach(module('ngRoute', function($compileProvider) { + element = null; + directive = $compileProvider.directive; + })); + + afterEach(function() { + if (element) { + dealoc(element); + } }); - }); + it('should allow access to directive controller from children when used in a replace template', function() { + var controller; + module(function($routeProvider) { + $routeProvider.when('/view', {templateUrl: 'view.html'}); + directive('template', function() { + return { + template: '
          ', + replace: true, + controller: function() { + this.flag = true; + } + }; + }); - it('should link directives on the same element after the content has been loaded', function() { - var contentOnLink; - module(function($compileProvider, $routeProvider) { - $routeProvider.when('/view', {template: 'someContent'}); - $compileProvider.directive('test', function() { - return { - link: function(scope, element) { - contentOnLink = element.text(); - } - }; + directive('test', function() { + return { + require: '^template', + link: function(scope, el, attr, ctrl) { + controller = ctrl; + } + }; + }); + }); + inject(function($compile, $rootScope, $httpBackend, $location) { + $httpBackend.expectGET('view.html').respond('
          '); + element = $compile('
          ')($rootScope); + $location.url('/view'); + $rootScope.$apply(); + $httpBackend.flush(); + expect(controller.flag).toBe(true); }); }); - inject(function($compile, $rootScope, $location) { - element = $compile('
          ')($rootScope); - $location.url('/view'); - $rootScope.$apply(); - expect(contentOnLink).toBe('someContent'); - }); - }); - it('should add the content to the element before compiling it', function() { - var root; - module(function($compileProvider, $routeProvider) { - $routeProvider.when('/view', {template: ''}); - $compileProvider.directive('test', function() { - return { - link: function(scope, element) { - root = element.parent().parent(); - } - }; + it('should compile its content correctly (although we remove it later)', function() { + var testElement; + module(function($compileProvider, $routeProvider) { + $routeProvider.when('/view', {template: ' '}); + var directive = $compileProvider.directive; + directive('test', function() { + return { + link: function(scope, element) { + testElement = element; + } + }; + }); + }); + inject(function($compile, $rootScope, $location) { + element = $compile('
          ')($rootScope); + $location.url('/view'); + $rootScope.$apply(); + expect(testElement[0].nodeName).toBe('DIV'); }); + }); - inject(function($compile, $rootScope, $location) { - element = $compile('
          ')($rootScope); - $location.url('/view'); - $rootScope.$apply(); - expect(root[0]).toBe(element[0]); + + it('should link directives on the same element after the content has been loaded', function() { + var contentOnLink; + module(function($compileProvider, $routeProvider) { + $routeProvider.when('/view', {template: 'someContent'}); + $compileProvider.directive('test', function() { + return { + link: function(scope, element) { + contentOnLink = element.text(); + } + }; + }); + }); + inject(function($compile, $rootScope, $location) { + element = $compile('
          ')($rootScope); + $location.url('/view'); + $rootScope.$apply(); + expect(contentOnLink).toBe('someContent'); + }); }); - }); -}); -describe('ngView animations', function() { - var body, element, $rootElement; - - beforeEach(module('ngRoute')); - - function html(content) { - $rootElement.html(content); - body.append($rootElement); - element = $rootElement.children().eq(0); - return element; - } - - beforeEach(module(function() { - // we need to run animation on attached elements; - return function(_$rootElement_) { - $rootElement = _$rootElement_; - body = angular.element(document.body); - }; - })); - - afterEach(function() { - dealoc(body); - dealoc(element); + it('should add the content to the element before compiling it', function() { + var root; + module(function($compileProvider, $routeProvider) { + $routeProvider.when('/view', {template: ''}); + $compileProvider.directive('test', function() { + return { + link: function(scope, element) { + root = element.parent().parent(); + } + }; + }); + }); + inject(function($compile, $rootScope, $location) { + element = $compile('
          ')($rootScope); + $location.url('/view'); + $rootScope.$apply(); + expect(root[0]).toBe(element[0]); + }); + }); }); + describe('animations', function() { + var body, element, $rootElement; - beforeEach(module(function($provide, $routeProvider) { - $routeProvider.when('/foo', {controller: angular.noop, templateUrl: '/foo.html'}); - $routeProvider.when('/bar', {controller: angular.noop, templateUrl: '/bar.html'}); - return function($templateCache) { - $templateCache.put('/foo.html', [200, '
          data
          ', {}]); - $templateCache.put('/bar.html', [200, '
          data2
          ', {}]); - }; - })); + beforeEach(module('ngRoute')); - describe('hooks', function() { - beforeEach(module('ngAnimate')); - beforeEach(module('ngAnimateMock')); + function html(content) { + $rootElement.html(content); + body.append($rootElement); + element = $rootElement.children().eq(0); + return element; + } - it('should fire off the enter animation', - inject(function($compile, $rootScope, $location, $timeout, $animate) { - element = $compile(html('
          '))($rootScope); + beforeEach(module(function() { + // we need to run animation on attached elements; + return function(_$rootElement_) { + $rootElement = _$rootElement_; + body = angular.element(window.document.body); + }; + })); - $location.path('/foo'); - $rootScope.$digest(); + afterEach(function() { + dealoc(body); + dealoc(element); + }); - var animation = $animate.queue.pop(); - expect(animation.event).toBe('enter'); - })); - it('should fire off the leave animation', - inject(function($compile, $rootScope, $location, $templateCache, $timeout, $animate) { + beforeEach(module(function($provide, $routeProvider) { + $routeProvider.when('/foo', {controller: angular.noop, templateUrl: '/foo.html'}); + $routeProvider.when('/bar', {controller: angular.noop, templateUrl: '/bar.html'}); + return function($templateCache) { + $templateCache.put('/foo.html', [200, '
          data
          ', {}]); + $templateCache.put('/bar.html', [200, '
          data2
          ', {}]); + }; + })); - var item; - $templateCache.put('/foo.html', [200, '
          foo
          ', {}]); - element = $compile(html('
          '))($rootScope); + describe('hooks', function() { + beforeEach(module('ngAnimate')); + beforeEach(module('ngAnimateMock')); - $location.path('/foo'); - $rootScope.$digest(); + it('should fire off the enter animation', + inject(function($compile, $rootScope, $location, $timeout, $animate) { + element = $compile(html('
          '))($rootScope); - $animate.triggerCallbacks(); + $location.path('/foo'); + $rootScope.$digest(); - $location.path('/'); - $rootScope.$digest(); + var animation = $animate.queue.pop(); + expect(animation.event).toBe('enter'); + })); - var animation = $animate.queue.pop(); - expect(animation.event).toBe('leave'); - })); + it('should fire off the leave animation', + inject(function($compile, $rootScope, $location, $templateCache, $timeout, $animate) { - it('should animate two separate ngView elements', - inject(function($compile, $rootScope, $templateCache, $location, $animate) { var item; - $rootScope.tpl = 'one'; + $templateCache.put('/foo.html', [200, '
          foo
          ', {}]); element = $compile(html('
          '))($rootScope); - $rootScope.$digest(); $location.path('/foo'); $rootScope.$digest(); - //we don't care about the enter animation for the first element - $animate.queue.pop(); - $location.path('/bar'); + $location.path('/'); $rootScope.$digest(); - var animationB = $animate.queue.pop(); - expect(animationB.event).toBe('leave'); - var itemB = animationB.args[0]; + var animation = $animate.queue.pop(); + expect(animation.event).toBe('leave'); + })); - var animationA = $animate.queue.pop(); - expect(animationA.event).toBe('enter'); - var itemA = animationA.args[0]; + it('should animate two separate ngView elements', + inject(function($compile, $rootScope, $templateCache, $location, $animate) { + var item; + $rootScope.tpl = 'one'; + element = $compile(html('
          '))($rootScope); + $rootScope.$digest(); - expect(itemA).not.toEqual(itemB); - }) - ); + $location.path('/foo'); + $rootScope.$digest(); - it('should render ngClass on ngView', - inject(function($compile, $rootScope, $templateCache, $animate, $location, $timeout) { + //we don't care about the enter animation for the first element + $animate.queue.pop(); - var item; - $rootScope.tpl = 'one'; - $rootScope.klass = 'classy'; - element = $compile(html('
          '))($rootScope); - $rootScope.$digest(); + $location.path('/bar'); + $rootScope.$digest(); - $location.path('/foo'); - $rootScope.$digest(); + var animationB = $animate.queue.pop(); + expect(animationB.event).toBe('leave'); + var itemB = animationB.args[0]; - //we don't care about the enter animation - $animate.queue.shift(); + var animationA = $animate.queue.pop(); + expect(animationA.event).toBe('enter'); + var itemA = animationA.args[0]; - var animation = $animate.queue.shift(); - expect(animation.event).toBe('addClass'); + expect(itemA).not.toEqual(itemB); + }) + ); - item = animation.element; - expect(item.hasClass('classy')).toBe(true); + it('should render ngClass on ngView', + inject(function($compile, $rootScope, $templateCache, $animate, $location) { - $rootScope.klass = 'boring'; - $rootScope.$digest(); + var item; + $rootScope.tpl = 'one'; + $rootScope.klass = 'classy'; + element = $compile(html('
          '))($rootScope); + $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('addClass'); - expect($animate.queue.shift().event).toBe('removeClass'); + $location.path('/foo'); + $rootScope.$digest(); + $animate.flush(); - $animate.triggerReflow(); + //we don't care about the enter animation + $animate.queue.shift(); - expect(item.hasClass('classy')).toBe(false); - expect(item.hasClass('boring')).toBe(true); + var animation = $animate.queue.shift(); + expect(animation.event).toBe('addClass'); - $location.path('/bar'); - $rootScope.$digest(); + item = animation.element; + expect(item.hasClass('classy')).toBe(true); - //we don't care about the enter animation - $animate.queue.shift(); + $rootScope.klass = 'boring'; + $rootScope.$digest(); - animation = $animate.queue.shift(); - item = animation.element; - expect(animation.event).toBe('leave'); + expect($animate.queue.shift().event).toBe('addClass'); + expect($animate.queue.shift().event).toBe('removeClass'); - expect($animate.queue.shift().event).toBe('addClass'); + $animate.flush(); - expect(item.hasClass('boring')).toBe(true); - }) - ); + expect(item.hasClass('classy')).toBe(false); + expect(item.hasClass('boring')).toBe(true); - it('should not double compile when the route changes', function() { + $location.path('/bar'); + $rootScope.$digest(); - var window; - module(function($routeProvider, $animateProvider, $provide) { - $routeProvider.when('/foo', {template: '
          {{i}}
          '}); - $routeProvider.when('/bar', {template: '
          {{i}}
          '}); - $animateProvider.register('.my-animation', function() { - return { - leave: function(element, done) { - done(); - } - }; + //we don't care about the enter animation + $animate.queue.shift(); + + animation = $animate.queue.shift(); + item = animation.element; + expect(animation.event).toBe('leave'); + + expect($animate.queue.shift().event).toBe('addClass'); + + expect(item.hasClass('boring')).toBe(true); + }) + ); + + it('should not double compile when the route changes', function() { + + var window; + module(function($routeProvider, $animateProvider, $provide) { + $routeProvider.when('/foo', {template: '
          {{i}}
          '}); + $routeProvider.when('/bar', {template: '
          {{i}}
          '}); + $animateProvider.register('.my-animation', function() { + return { + leave: function(element, done) { + done(); + } + }; + }); }); - }); - inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) { - element = $compile(html('
          '))($rootScope); - $animate.enabled(true); + inject(function($rootScope, $compile, $location, $route, $timeout, $rootElement, $sniffer, $animate) { + element = $compile(html('
          '))($rootScope); + $animate.enabled(true); - $location.path('/foo'); - $rootScope.$digest(); + $location.path('/foo'); + $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); //ngView - expect($animate.queue.shift().event).toBe('enter'); //repeat 1 - expect($animate.queue.shift().event).toBe('enter'); //repeat 2 + expect($animate.queue.shift().event).toBe('enter'); //ngView + expect($animate.queue.shift().event).toBe('enter'); //repeat 1 + expect($animate.queue.shift().event).toBe('enter'); //repeat 2 - expect(element.text()).toEqual('12'); + expect(element.text()).toEqual('12'); - $location.path('/bar'); - $rootScope.$digest(); + $location.path('/bar'); + $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); //ngView new - expect($animate.queue.shift().event).toBe('leave'); //ngView old + expect($animate.queue.shift().event).toBe('enter'); //ngView new + expect($animate.queue.shift().event).toBe('leave'); //ngView old - $rootScope.$digest(); + $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 3 - expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 4 + expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 3 + expect($animate.queue.shift().event).toBe('enter'); //ngRepeat 4 - expect(element.text()).toEqual('34'); + $animate.flush(); - function n(text) { - return text.replace(/\r\n/m, '').replace(/\r\n/m, ''); - } + expect(element.text()).toEqual('34'); + + function n(text) { + return text.replace(/\r\n/m, '').replace(/\r\n/m, ''); + } + }); }); + + it('should destroy the previous leave animation if a new one takes place', + inject(function($compile, $rootScope, $animate, $location, $timeout) { + var $scope = $rootScope.$new(); + element = $compile(html( + '
          ' + + '
          ' + + '
          ' + ))($scope); + + $scope.$apply('value = true'); + + $location.path('/bar'); + $rootScope.$digest(); + + var destroyed, inner = element.children(0); + inner.on('$destroy', function() { + destroyed = true; + }); + + $location.path('/foo'); + $rootScope.$digest(); + + $location.path('/bar'); + $rootScope.$digest(); + + $location.path('/bar'); + $rootScope.$digest(); + + expect(destroyed).toBe(true); + }) + ); }); - it('should destroy the previous leave animation if a new one takes place', - inject(function($compile, $rootScope, $animate, $location, $timeout) { - var $scope = $rootScope.$new(); - element = $compile(html( - '
          ' + - '
          ' + - '
          ' - ))($scope); - $scope.$apply('value = true'); + describe('autoscroll', function() { + var autoScrollSpy; - $location.path('/bar'); - $rootScope.$digest(); + function spyOnAnchorScroll() { + return function($provide, $routeProvider) { + autoScrollSpy = jasmine.createSpy('$anchorScroll'); + $provide.value('$anchorScroll', autoScrollSpy); + $routeProvider.when('/foo', { + controller: angular.noop, + template: '
          ' + }); + }; + } - var destroyed, inner = element.children(0); - inner.on('$destroy', function() { - destroyed = true; - }); + function spyOnAnimateEnter() { + return function($animate) { + spyOn($animate, 'enter').and.callThrough(); + }; + } - $location.path('/foo'); - $rootScope.$digest(); + function compileAndLink(tpl) { + return function($compile, $rootScope, $location) { + element = $compile(tpl)($rootScope); + }; + } - $location.path('/bar'); + beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock')); + beforeEach(inject(spyOnAnimateEnter())); + + it('should call $anchorScroll if autoscroll attribute is present', inject( + compileAndLink('
          '), + function($rootScope, $animate, $timeout, $location) { + + $location.path('/foo'); $rootScope.$digest(); - $location.path('/bar'); + $animate.flush(); $rootScope.$digest(); - expect(destroyed).toBe(true); - }) - ); - }); + expect($animate.queue.shift().event).toBe('enter'); + expect(autoScrollSpy).toHaveBeenCalledOnce(); + })); - describe('autoscroll', function() { - var autoScrollSpy; + it('should call $anchorScroll if autoscroll evaluates to true', inject( + compileAndLink('
          '), + function($rootScope, $animate, $timeout, $location) { - function spyOnAnchorScroll() { - return function($provide, $routeProvider) { - autoScrollSpy = jasmine.createSpy('$anchorScroll'); - $provide.value('$anchorScroll', autoScrollSpy); - $routeProvider.when('/foo', { - controller: angular.noop, - template: '
          ' - }); - }; - } + $rootScope.value = true; + $location.path('/foo'); + $rootScope.$digest(); - function spyOnAnimateEnter() { - return function($animate) { - spyOn($animate, 'enter').andCallThrough(); - }; - } + $animate.flush(); + $rootScope.$digest(); - function compileAndLink(tpl) { - return function($compile, $rootScope, $location) { - element = $compile(tpl)($rootScope); - }; - } + expect($animate.queue.shift().event).toBe('enter'); + expect(autoScrollSpy).toHaveBeenCalledOnce(); + })); - beforeEach(module(spyOnAnchorScroll(), 'ngAnimateMock')); - beforeEach(inject(spyOnAnimateEnter())); - it('should call $anchorScroll if autoscroll attribute is present', inject( - compileAndLink('
          '), - function($rootScope, $animate, $timeout, $location) { + it('should not call $anchorScroll if autoscroll attribute is not present', inject( + compileAndLink('
          '), + function($rootScope, $location, $animate, $timeout) { - $location.path('/foo'); - $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + $location.path('/foo'); + $rootScope.$digest(); + expect($animate.queue.shift().event).toBe('enter'); - expect(autoScrollSpy).toHaveBeenCalledOnce(); - })); + expect(autoScrollSpy).not.toHaveBeenCalled(); + })); - it('should call $anchorScroll if autoscroll evaluates to true', inject( - compileAndLink('
          '), - function($rootScope, $animate, $timeout, $location) { + it('should not call $anchorScroll if autoscroll evaluates to false', inject( + compileAndLink('
          '), + function($rootScope, $location, $animate, $timeout) { - $rootScope.value = true; - $location.path('/foo'); - $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + $rootScope.value = false; + $location.path('/foo'); + $rootScope.$digest(); + expect($animate.queue.shift().event).toBe('enter'); - expect(autoScrollSpy).toHaveBeenCalledOnce(); - })); + expect(autoScrollSpy).not.toHaveBeenCalled(); + })); - it('should not call $anchorScroll if autoscroll attribute is not present', inject( - compileAndLink('
          '), + it('should only call $anchorScroll after the "enter" animation completes', inject( + compileAndLink('
          '), function($rootScope, $location, $animate, $timeout) { + $location.path('/foo'); - $location.path('/foo'); - $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + expect($animate.enter).not.toHaveBeenCalled(); + $rootScope.$digest(); - expect(autoScrollSpy).not.toHaveBeenCalled(); - })); + expect(autoScrollSpy).not.toHaveBeenCalled(); + expect($animate.queue.shift().event).toBe('enter'); - it('should not call $anchorScroll if autoscroll evaluates to false', inject( - compileAndLink('
          '), - function($rootScope, $location, $animate, $timeout) { + $animate.flush(); + $rootScope.$digest(); - $rootScope.value = false; - $location.path('/foo'); - $rootScope.$digest(); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + expect($animate.enter).toHaveBeenCalledOnce(); + expect(autoScrollSpy).toHaveBeenCalledOnce(); + } + )); + }); + }); - expect(autoScrollSpy).not.toHaveBeenCalled(); - })); + describe('in async template', function() { + beforeEach(module('ngRoute')); + beforeEach(module(function($compileProvider, $provide, $routeProvider) { + $compileProvider.directive('asyncView', function() { + return {templateUrl: 'async-view.html'}; + }); + $provide.decorator('$templateRequest', function($timeout) { + return function() { + return $timeout(angular.identity, 500, false, ''); + }; + }); - it('should only call $anchorScroll after the "enter" animation completes', inject( - compileAndLink('
          '), - function($rootScope, $location, $animate, $timeout) { - $location.path('/foo'); + $routeProvider.when('/', {template: 'Hello, world!'}); + })); - expect($animate.enter).not.toHaveBeenCalled(); - $rootScope.$digest(); - expect(autoScrollSpy).not.toHaveBeenCalled(); + it('should work correctly upon initial page load', + // Injecting `$location` here is necessary, so that it gets instantiated early + inject(function($compile, $location, $rootScope, $timeout) { + var elem = $compile('')($rootScope); + $rootScope.$digest(); + $timeout.flush(500); - expect($animate.queue.shift().event).toBe('enter'); - $animate.triggerCallbacks(); + expect(elem.text()).toBe('Hello, world!'); - expect($animate.enter).toHaveBeenCalledOnce(); - expect(autoScrollSpy).toHaveBeenCalledOnce(); - } - )); + dealoc(elem); + }) + ); }); }); diff --git a/test/ngRoute/routeParamsSpec.js b/test/ngRoute/routeParamsSpec.js index 7c10a922302d..88b27dd8409d 100644 --- a/test/ngRoute/routeParamsSpec.js +++ b/test/ngRoute/routeParamsSpec.js @@ -72,10 +72,50 @@ describe('$routeParams', function() { $location.path('/qux//bazValue'); $rootScope.$digest(); - expect($routeParams).toEqual({baz: 'bazValue', bar: undefined}); + expect($routeParams).toEqual({baz: 'bazValue'}); }); }); + it('should correctly extract path params containing hashes and/or question marks', function() { + module(function($routeProvider) { + $routeProvider.when('/foo/:bar', {}); + $routeProvider.when('/zoo/:bar/:baz/:qux', {}); + }); + + inject(function($location, $rootScope, $routeParams) { + $location.path('/foo/bar?baz'); + $rootScope.$digest(); + expect($routeParams).toEqual({bar: 'bar?baz'}); + + $location.path('/foo/bar?baz=val'); + $rootScope.$digest(); + expect($routeParams).toEqual({bar: 'bar?baz=val'}); + + $location.path('/foo/bar#baz'); + $rootScope.$digest(); + expect($routeParams).toEqual({bar: 'bar#baz'}); + + $location.path('/foo/bar?baz#qux'); + $rootScope.$digest(); + expect($routeParams).toEqual({bar: 'bar?baz#qux'}); + + $location.path('/foo/bar?baz=val#qux'); + $rootScope.$digest(); + expect($routeParams).toEqual({bar: 'bar?baz=val#qux'}); + + $location.path('/foo/bar#baz?qux'); + $rootScope.$digest(); + expect($routeParams).toEqual({bar: 'bar#baz?qux'}); + + $location.path('/zoo/bar?p1=v1#h1/baz?p2=v2#h2/qux?p3=v3#h3'); + $rootScope.$digest(); + expect($routeParams).toEqual({ + bar: 'bar?p1=v1#h1', + baz: 'baz?p2=v2#h2', + qux: 'qux?p3=v3#h3' + }); + }); + }); }); diff --git a/test/ngRoute/routeSpec.js b/test/ngRoute/routeSpec.js index 1839fd0e17cf..fa31d4124651 100644 --- a/test/ngRoute/routeSpec.js +++ b/test/ngRoute/routeSpec.js @@ -1,5 +1,58 @@ 'use strict'; +describe('$routeProvider', function() { + var $routeProvider; + + beforeEach(module('ngRoute')); + beforeEach(module(function(_$routeProvider_) { + $routeProvider = _$routeProvider_; + $routeProvider.when('/foo', {template: 'Hello, world!'}); + })); + + + it('should support enabling/disabling automatic instantiation upon initial load', + inject(function() { + expect($routeProvider.eagerInstantiationEnabled(true)).toBe($routeProvider); + expect($routeProvider.eagerInstantiationEnabled()).toBe(true); + + expect($routeProvider.eagerInstantiationEnabled(false)).toBe($routeProvider); + expect($routeProvider.eagerInstantiationEnabled()).toBe(false); + + expect($routeProvider.eagerInstantiationEnabled(true)).toBe($routeProvider); + expect($routeProvider.eagerInstantiationEnabled()).toBe(true); + }) + ); + + + it('should automatically instantiate `$route` upon initial load', function() { + inject(function($location, $rootScope) { + $location.path('/foo'); + $rootScope.$digest(); + }); + + inject(function($route) { + expect($route.current).toBeDefined(); + }); + }); + + + it('should not automatically instantiate `$route` if disabled', function() { + module(function($routeProvider) { + $routeProvider.eagerInstantiationEnabled(false); + }); + + inject(function($location, $rootScope) { + $location.path('/foo'); + $rootScope.$digest(); + }); + + inject(function($route) { + expect($route.current).toBeUndefined(); + }); + }); +}); + + describe('$route', function() { var $httpBackend, element; @@ -12,8 +65,8 @@ describe('$route', function() { $httpBackend.when('GET', 'Chapter.html').respond('chapter'); $httpBackend.when('GET', 'test.html').respond('test'); $httpBackend.when('GET', 'foo.html').respond('foo'); - $httpBackend.when('GET', 'baz.html').respond('baz'); $httpBackend.when('GET', 'bar.html').respond('bar'); + $httpBackend.when('GET', 'baz.html').respond('baz'); $httpBackend.when('GET', 'http://example.com/trusted-template.html').respond('cross domain trusted template'); $httpBackend.when('GET', '404.html').respond('not found'); }; @@ -23,6 +76,7 @@ describe('$route', function() { dealoc(element); }); + it('should allow cancellation via $locationChangeStart via $routeChangeStart', function() { module(function($routeProvider) { $routeProvider.when('/Edit', { @@ -140,7 +194,7 @@ describe('$route', function() { $location.path('/NONE'); $rootScope.$digest(); expect(log).toEqual('before();after();'); - expect($route.current).toEqual(null); + expect($route.current).toEqual(undefined); }); }); @@ -198,7 +252,7 @@ describe('$route', function() { $location.path('/NONE'); $rootScope.$digest(); expect(log).toEqual('before();after();'); - expect($route.current).toEqual(null); + expect($route.current).toEqual(undefined); }); }); @@ -251,7 +305,7 @@ describe('$route', function() { $location.path('/BLANK'); $rootScope.$digest(); expect(log).toEqual('before();after();'); - expect($route.current).toEqual(null); + expect($route.current).toEqual(undefined); log = ''; $location.path('/Book2/Moby/one/two/Chapter/Intro').search('p=123'); @@ -263,7 +317,7 @@ describe('$route', function() { $location.path('/BOOK2/Moby/one/two/CHAPTER/Intro').search('p=123'); $rootScope.$digest(); expect(log).toEqual('before();after();'); - expect($route.current).toEqual(null); + expect($route.current).toEqual(undefined); }); }); @@ -303,7 +357,7 @@ describe('$route', function() { event.preventDefault(); }); - $rootScope.$on('$beforeRouteChange', function(event) { + $rootScope.$on('$routeChangeSuccess', function(event) { throw new Error('Should not get here'); }); @@ -350,7 +404,7 @@ describe('$route', function() { expect($route.current).toBeDefined(); })); - it("should use route params inherited from prototype chain", function() { + it('should use route params inherited from prototype chain', function() { function BaseRoute() {} BaseRoute.prototype.templateUrl = 'foo.html'; @@ -403,7 +457,7 @@ describe('$route', function() { $rootScope.$on('$routeChangeStart', callback); $location.path('/test'); $rootScope.$digest(); - callback.reset(); + callback.calls.reset(); $location.search({any: true}); $rootScope.$digest(); @@ -521,7 +575,7 @@ describe('$route', function() { expect($route.current.controller).toBe(NotFoundCtrl); expect(onChangeSpy).toHaveBeenCalled(); - onChangeSpy.reset(); + onChangeSpy.calls.reset(); $location.path('/foo'); $rootScope.$digest(); @@ -540,7 +594,7 @@ describe('$route', function() { inject(function($route, $location, $rootScope) { var currentRoute, nextRoute, - onChangeSpy = jasmine.createSpy('onChange').andCallFake(function(e, next) { + onChangeSpy = jasmine.createSpy('onChange').and.callFake(function(e, next) { currentRoute = $route.current; nextRoute = next; }); @@ -560,7 +614,7 @@ describe('$route', function() { expect(nextRoute.templateUrl).toBe('404.html'); expect($route.current.templateUrl).toBe('404.html'); expect(onChangeSpy).toHaveBeenCalled(); - onChangeSpy.reset(); + onChangeSpy.calls.reset(); // match regular route $location.path('/foo'); @@ -570,7 +624,7 @@ describe('$route', function() { expect(nextRoute.templateUrl).toBe('foo.html'); expect($route.current.templateUrl).toEqual('foo.html'); expect(onChangeSpy).toHaveBeenCalled(); - onChangeSpy.reset(); + onChangeSpy.calls.reset(); // match otherwise route again $location.path('/anotherUnknownRoute'); @@ -729,18 +783,27 @@ describe('$route', function() { }); inject(function($route, $location, $rootScope) { + var onError = jasmine.createSpy('onError'); + var onSuccess = jasmine.createSpy('onSuccess'); + + $rootScope.$on('$routeChangeError', onError); + $rootScope.$on('$routeChangeSuccess', onSuccess); + $location.path('/foo'); - expect(function() { - $rootScope.$digest(); - }).toThrowMinErr('$sce', 'insecurl', 'Blocked loading resource from url not allowed by ' + - '$sceDelegate policy. URL: http://example.com/foo.html'); + $rootScope.$digest(); + + expect(onSuccess).not.toHaveBeenCalled(); + expect(onError).toHaveBeenCalled(); + expect(onError.calls.mostRecent().args[3]).toEqualMinErr('$sce', 'insecurl', + 'Blocked loading resource from url not allowed by $sceDelegate policy. ' + + 'URL: http://example.com/foo.html'); }); }); it('should load cross domain templates that are trusted', function() { module(function($routeProvider, $sceDelegateProvider) { $routeProvider.when('/foo', { templateUrl: 'http://example.com/foo.html' }); - $sceDelegateProvider.resourceUrlWhitelist([/^https:\/\/fanyv88.com:443\/http\/example\.com\/foo\.html$/]); + $sceDelegateProvider.trustedResourceUrlList([/^https:\/\/fanyv88.com:443\/http\/example\.com\/foo\.html$/]); }); inject(function($route, $location, $rootScope) { @@ -829,7 +892,8 @@ describe('$route', function() { $rootScope.$digest(); $httpBackend.flush(); - expect($exceptionHandler.errors.pop().message).toContain("[$compile:tpload] Failed to load template: r1.html"); + expect($exceptionHandler.errors.pop()). + toEqualMinErr('$templateRequest', 'tpload', 'Failed to load template: r1.html'); $httpBackend.expectGET('r2.html').respond(''); $location.path('/r2'); @@ -850,8 +914,7 @@ describe('$route', function() { it('should catch local factory errors', function() { var myError = new Error('MyError'); - module(function($routeProvider, $exceptionHandlerProvider) { - $exceptionHandlerProvider.mode('log'); + module(function($routeProvider) { $routeProvider.when('/locals', { resolve: { a: function($q) { @@ -861,10 +924,14 @@ describe('$route', function() { }); }); - inject(function($location, $route, $rootScope, $exceptionHandler) { + inject(function($location, $route, $rootScope) { + spyOn($rootScope, '$broadcast').and.callThrough(); + $location.path('/locals'); $rootScope.$digest(); - expect($exceptionHandler.errors).toEqual([myError]); + + expect($rootScope.$broadcast).toHaveBeenCalledWith( + '$routeChangeError', jasmine.any(Object), undefined, myError); }); }); }); @@ -900,348 +967,1325 @@ describe('$route', function() { }); - describe('redirection', function() { - it('should support redirection via redirectTo property by updating $location', function() { + it('should not get affected by modifying the route definition object after route registration', + function() { module(function($routeProvider) { - $routeProvider.when('/', {redirectTo: '/foo'}); - $routeProvider.when('/foo', {templateUrl: 'foo.html'}); - $routeProvider.when('/bar', {templateUrl: 'bar.html'}); - $routeProvider.when('/baz', {redirectTo: '/bar'}); - $routeProvider.otherwise({templateUrl: '404.html'}); - }); - - inject(function($route, $location, $rootScope) { - var onChangeSpy = jasmine.createSpy('onChange'); + var rdo = {}; - $rootScope.$on('$routeChangeStart', onChangeSpy); - expect($route.current).toBeUndefined(); - expect(onChangeSpy).not.toHaveBeenCalled(); + rdo.templateUrl = 'foo.html'; + $routeProvider.when('/foo', rdo); - $location.path('/'); - $rootScope.$digest(); - expect($location.path()).toBe('/foo'); - expect($route.current.templateUrl).toBe('foo.html'); - expect(onChangeSpy.callCount).toBe(2); + rdo.templateUrl = 'bar.html'; + $routeProvider.when('/bar', rdo); + }); - onChangeSpy.reset(); - $location.path('/baz'); + inject(function($location, $rootScope, $route) { + $location.path('/bar'); $rootScope.$digest(); expect($location.path()).toBe('/bar'); expect($route.current.templateUrl).toBe('bar.html'); - expect(onChangeSpy.callCount).toBe(2); + + $location.path('/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($route.current.templateUrl).toBe('foo.html'); }); - }); + } + ); - it('should interpolate route vars in the redirected path from original path', function() { - module(function($routeProvider) { - $routeProvider.when('/foo/:id/foo/:subid/:extraId', {redirectTo: '/bar/:id/:subid/23'}); - $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); - $routeProvider.when('/baz/:id/:path*', {redirectTo: '/path/:path/:id'}); - $routeProvider.when('/path/:path*/:id', {templateUrl: 'foo.html'}); - }); + it('should use the property values of the passed in route definition object directly', + function() { + var $routeProvider; - inject(function($route, $location, $rootScope) { - $location.path('/foo/id1/foo/subid3/gah'); - $rootScope.$digest(); + module(function(_$routeProvider_) { + $routeProvider = _$routeProvider_; + }); - expect($location.path()).toEqual('/bar/id1/subid3/23'); - expect($location.search()).toEqual({extraId: 'gah'}); - expect($route.current.templateUrl).toEqual('bar.html'); + inject(function($location, $rootScope, $route, $sce) { + var sceWrappedUrl = $sce.trustAsResourceUrl('foo.html'); + $routeProvider.when('/foo', {templateUrl: sceWrappedUrl}); - $location.path('/baz/1/foovalue/barvalue'); + $location.path('/foo'); $rootScope.$digest(); - expect($location.path()).toEqual('/path/foovalue/barvalue/1'); - expect($route.current.templateUrl).toEqual('foo.html'); + expect($location.path()).toBe('/foo'); + expect($route.current.templateUrl).toBe(sceWrappedUrl); }); - }); + } + ); - it('should interpolate route vars in the redirected path from original search', function() { - module(function($routeProvider) { - $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); - $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id/:subid/99'}); - }); + it('should support custom `$sce` implementations', function() { + function MySafeResourceUrl(val) { + var self = this; + this._val = val; + this.getVal = function() { + return (this !== self) ? null : this._val; + }; + } - inject(function($route, $location, $rootScope) { - $location.path('/foo/id3/eId').search('subid=sid1&appended=true'); - $rootScope.$digest(); + var $routeProvider; + + module(function($provide, _$routeProvider_) { + $routeProvider = _$routeProvider_; - expect($location.path()).toEqual('/bar/id3/sid1/99'); - expect($location.search()).toEqual({appended: 'true', extra: 'eId'}); - expect($route.current.templateUrl).toEqual('bar.html'); + $provide.decorator('$sce', function($delegate) { + function getVal(v) { return v.getVal ? v.getVal() : v; } + $delegate.trustAsResourceUrl = function(url) { return new MySafeResourceUrl(url); }; + $delegate.getTrustedResourceUrl = function(v) { return getVal(v); }; + $delegate.valueOf = function(v) { return getVal(v); }; + return $delegate; }); }); + inject(function($location, $rootScope, $route, $sce) { + $routeProvider.when('/foo', {templateUrl: $sce.trustAsResourceUrl('foo.html')}); - it('should properly interpolate optional and eager route vars ' + - 'when redirecting from path with trailing slash', function() { - module(function($routeProvider) { - $routeProvider.when('/foo/:id?/:subid?', {templateUrl: 'foo.html'}); - $routeProvider.when('/bar/:id*/:subid', {templateUrl: 'bar.html'}); - }); + $location.path('/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($sce.valueOf($route.current.templateUrl)).toBe('foo.html'); + }); + }); - inject(function($location, $rootScope, $route) { - $location.path('/foo/id1/subid2/'); - $rootScope.$digest(); - expect($location.path()).toEqual('/foo/id1/subid2'); - expect($route.current.templateUrl).toEqual('foo.html'); + describe('redirection', function() { + describe('via `redirectTo`', function() { + it('should support redirection via redirectTo property by updating $location', function() { + module(function($routeProvider) { + $routeProvider.when('/', {redirectTo: '/foo'}); + $routeProvider.when('/foo', {templateUrl: 'foo.html'}); + $routeProvider.when('/bar', {templateUrl: 'bar.html'}); + $routeProvider.when('/baz', {redirectTo: '/bar'}); + $routeProvider.otherwise({templateUrl: '404.html'}); + }); - $location.path('/bar/id1/extra/subid2/'); - $rootScope.$digest(); + inject(function($route, $location, $rootScope) { + var onChangeSpy = jasmine.createSpy('onChange'); - expect($location.path()).toEqual('/bar/id1/extra/subid2'); - expect($route.current.templateUrl).toEqual('bar.html'); + $rootScope.$on('$routeChangeStart', onChangeSpy); + expect($route.current).toBeUndefined(); + expect(onChangeSpy).not.toHaveBeenCalled(); + + $location.path('/'); + $rootScope.$digest(); + expect($location.path()).toBe('/foo'); + expect($route.current.templateUrl).toBe('foo.html'); + expect(onChangeSpy).toHaveBeenCalledTimes(2); + + onChangeSpy.calls.reset(); + $location.path('/baz'); + $rootScope.$digest(); + expect($location.path()).toBe('/bar'); + expect($route.current.templateUrl).toBe('bar.html'); + expect(onChangeSpy).toHaveBeenCalledTimes(2); + }); }); - }); - it('should allow custom redirectTo function to be used', function() { - function customRedirectFn(routePathParams, path, search) { - expect(routePathParams).toEqual({id: 'id3'}); - expect(path).toEqual('/foo/id3'); - expect(search).toEqual({ subid: 'sid1', appended: 'true' }); - return '/custom'; - } + it('should interpolate route vars in the redirected path from original path', function() { + module(function($routeProvider) { + $routeProvider.when('/foo/:id/foo/:subid/:extraId', {redirectTo: '/bar/:id/:subid/23'}); + $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); + $routeProvider.when('/baz/:id/:path*', {redirectTo: '/path/:path/:id'}); + $routeProvider.when('/path/:path*/:id', {templateUrl: 'foo.html'}); + }); - module(function($routeProvider) { - $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); - $routeProvider.when('/foo/:id', {redirectTo: customRedirectFn}); - }); + inject(function($route, $location, $rootScope) { + $location.path('/foo/id1/foo/subid3/gah'); + $rootScope.$digest(); - inject(function($route, $location, $rootScope) { - $location.path('/foo/id3').search('subid=sid1&appended=true'); - $rootScope.$digest(); + expect($location.path()).toEqual('/bar/id1/subid3/23'); + expect($location.search()).toEqual({extraId: 'gah'}); + expect($route.current.templateUrl).toEqual('bar.html'); - expect($location.path()).toEqual('/custom'); + $location.path('/baz/1/foovalue/barvalue'); + $rootScope.$digest(); + expect($location.path()).toEqual('/path/foovalue/barvalue/1'); + expect($route.current.templateUrl).toEqual('foo.html'); + }); }); - }); - it('should replace the url when redirecting', function() { - module(function($routeProvider) { - $routeProvider.when('/bar/:id', {templateUrl: 'bar.html'}); - $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id'}); - }); - inject(function($browser, $route, $location, $rootScope) { - var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').andCallThrough(); + it('should interpolate route vars in the redirected path from original search', function() { + module(function($routeProvider) { + $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); + $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id/:subid/99'}); + }); - $location.path('/foo/id3/eId'); - $rootScope.$digest(); + inject(function($route, $location, $rootScope) { + $location.path('/foo/id3/eId').search('subid=sid1&appended=true'); + $rootScope.$digest(); - expect($location.path()).toEqual('/bar/id3'); - expect($browserUrl.mostRecentCall.args) - .toEqual(['http://server/#/bar/id3?extra=eId', true, null]); + expect($location.path()).toEqual('/bar/id3/sid1/99'); + expect($location.search()).toEqual({appended: 'true', extra: 'eId'}); + expect($route.current.templateUrl).toEqual('bar.html'); + }); }); - }); - }); - describe('reloadOnSearch', function() { - it('should reload a route when reloadOnSearch is enabled and .search() changes', function() { - var reloaded = jasmine.createSpy('route reload'); - - module(function($routeProvider) { - $routeProvider.when('/foo', {controller: angular.noop}); - }); + it('should properly process route params which are both eager and optional', function() { + module(function($routeProvider) { + $routeProvider.when('/foo/:param1*?/:param2', {templateUrl: 'foo.html'}); + }); - inject(function($route, $location, $rootScope, $routeParams) { - $rootScope.$on('$routeChangeStart', reloaded); - $location.path('/foo'); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect($routeParams).toEqual({}); - reloaded.reset(); + inject(function($location, $rootScope, $route) { + $location.path('/foo/bar1/bar2/bar3/baz'); + $rootScope.$digest(); - // trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(reloaded).toHaveBeenCalled(); - expect($routeParams).toEqual({foo:'bar'}); - }); - }); + expect($location.path()).toEqual('/foo/bar1/bar2/bar3/baz'); + expect($route.current.params.param1).toEqual('bar1/bar2/bar3'); + expect($route.current.params.param2).toEqual('baz'); + expect($route.current.templateUrl).toEqual('foo.html'); + $location.path('/foo/baz'); + $rootScope.$digest(); - it('should not reload a route when reloadOnSearch is disabled and only .search() changes', function() { - var routeChange = jasmine.createSpy('route change'), - routeUpdate = jasmine.createSpy('route update'); + expect($location.path()).toEqual('/foo/baz'); + expect($route.current.params.param1).toEqual(undefined); + expect($route.current.params.param2).toEqual('baz'); + expect($route.current.templateUrl).toEqual('foo.html'); - module(function($routeProvider) { - $routeProvider.when('/foo', {controller: angular.noop, reloadOnSearch: false}); + }); }); - inject(function($route, $location, $rootScope) { - $rootScope.$on('$routeChangeStart', routeChange); - $rootScope.$on('$routeChangeSuccess', routeChange); - $rootScope.$on('$routeUpdate', routeUpdate); - - expect(routeChange).not.toHaveBeenCalled(); - $location.path('/foo'); - $rootScope.$digest(); - expect(routeChange).toHaveBeenCalled(); - expect(routeChange.callCount).toBe(2); - expect(routeUpdate).not.toHaveBeenCalled(); - routeChange.reset(); + it('should properly interpolate optional and eager route vars ' + + 'when redirecting from path with trailing slash', function() { + module(function($routeProvider) { + $routeProvider.when('/foo/:id?/:subid?', {templateUrl: 'foo.html'}); + $routeProvider.when('/bar/:id*/:subid', {templateUrl: 'bar.html'}); + }); - // don't trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(routeChange).not.toHaveBeenCalled(); - expect(routeUpdate).toHaveBeenCalled(); - }); - }); + inject(function($location, $rootScope, $route) { + $location.path('/foo/id1/subid2/'); + $rootScope.$digest(); + expect($location.path()).toEqual('/foo/id1/subid2'); + expect($route.current.templateUrl).toEqual('foo.html'); - it('should reload reloadOnSearch route when url differs only in route path param', function() { - var routeChange = jasmine.createSpy('route change'); + $location.path('/bar/id1/extra/subid2/'); + $rootScope.$digest(); - module(function($routeProvider) { - $routeProvider.when('/foo/:fooId', {controller: angular.noop, reloadOnSearch: false}); + expect($location.path()).toEqual('/bar/id1/extra/subid2'); + expect($route.current.templateUrl).toEqual('bar.html'); + }); }); - inject(function($route, $location, $rootScope) { - $rootScope.$on('$routeChangeStart', routeChange); - $rootScope.$on('$routeChangeSuccess', routeChange); - expect(routeChange).not.toHaveBeenCalled(); + it('should allow custom redirectTo function to be used', function() { + function customRedirectFn(routePathParams, path, search) { + expect(routePathParams).toEqual({id: 'id3'}); + expect(path).toEqual('/foo/id3'); + expect(search).toEqual({subid: 'sid1', appended: 'true'}); + return '/custom'; + } - $location.path('/foo/aaa'); - $rootScope.$digest(); - expect(routeChange).toHaveBeenCalled(); - expect(routeChange.callCount).toBe(2); - routeChange.reset(); + module(function($routeProvider) { + $routeProvider.when('/foo/:id', {redirectTo: customRedirectFn}); + }); - $location.path('/foo/bbb'); - $rootScope.$digest(); - expect(routeChange).toHaveBeenCalled(); - expect(routeChange.callCount).toBe(2); - routeChange.reset(); + inject(function($route, $location, $rootScope) { + $location.path('/foo/id3').search('subid=sid1&appended=true'); + $rootScope.$digest(); - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(routeChange).not.toHaveBeenCalled(); + expect($location.path()).toEqual('/custom'); + }); }); - }); - it('should update params when reloadOnSearch is disabled and .search() changes', function() { - var routeParamsWatcher = jasmine.createSpy('routeParamsWatcher'); + it('should broadcast `$routeChangeError` when redirectTo throws', function() { + var error = new Error('Test'); - module(function($routeProvider) { - $routeProvider.when('/foo', {controller: angular.noop}); - $routeProvider.when('/bar/:barId', {controller: angular.noop, reloadOnSearch: false}); - }); + module(function($routeProvider) { + $routeProvider.when('/foo', {redirectTo: function() { throw error; }}); + }); - inject(function($route, $location, $rootScope, $routeParams) { - $rootScope.$watch(function() { - return $routeParams; - }, function(value) { - routeParamsWatcher(value); - }, true); + inject(function($exceptionHandler, $location, $rootScope, $route) { + spyOn($rootScope, '$broadcast').and.callThrough(); - expect(routeParamsWatcher).not.toHaveBeenCalled(); + $location.path('/foo'); + $rootScope.$digest(); - $location.path('/foo'); - $rootScope.$digest(); - expect(routeParamsWatcher).toHaveBeenCalledWith({}); - routeParamsWatcher.reset(); + var lastCallArgs = $rootScope.$broadcast.calls.mostRecent().args; + expect(lastCallArgs[0]).toBe('$routeChangeError'); + expect(lastCallArgs[3]).toBe(error); + }); + }); - // trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(routeParamsWatcher).toHaveBeenCalledWith({foo: 'bar'}); - routeParamsWatcher.reset(); - $location.path('/bar/123').search({}); - $rootScope.$digest(); - expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123'}); - routeParamsWatcher.reset(); + it('should replace the url when redirecting', function() { + module(function($routeProvider) { + $routeProvider.when('/bar/:id', {templateUrl: 'bar.html'}); + $routeProvider.when('/foo/:id/:extra', {redirectTo: '/bar/:id'}); + }); + inject(function($browser, $route, $location, $rootScope) { + var $browserUrl = spyOnlyCallsWithArgs($browser, 'url').and.callThrough(); - // don't trigger reload - $location.search({foo: 'bar'}); - $rootScope.$digest(); - expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123', foo: 'bar'}); + $location.path('/foo/id3/eId'); + $rootScope.$digest(); + + expect($location.path()).toEqual('/bar/id3'); + expect($browserUrl.calls.mostRecent().args) + .toEqual(['http://server/#!/bar/id3?extra=eId', true, null]); + }); }); - }); - it('should allow using a function as a template', function() { - var customTemplateWatcher = jasmine.createSpy('customTemplateWatcher'); + it('should not process route bits', function() { + var firstController = jasmine.createSpy('first controller spy'); + var firstTemplate = jasmine.createSpy('first template spy').and.returnValue('redirected view'); + var firstResolve = jasmine.createSpy('first resolve spy'); + var secondController = jasmine.createSpy('second controller spy'); + var secondTemplate = jasmine.createSpy('second template spy').and.returnValue('redirected view'); + var secondResolve = jasmine.createSpy('second resolve spy'); + module(function($routeProvider) { + $routeProvider.when('/redirect', { + template: firstTemplate, + redirectTo: '/redirected', + resolve: { value: firstResolve }, + controller: firstController + }); + $routeProvider.when('/redirected', { + template: secondTemplate, + resolve: { value: secondResolve }, + controller: secondController + }); + }); + inject(function($route, $location, $rootScope, $compile) { + var element = $compile('
          ')($rootScope); + $location.path('/redirect'); + $rootScope.$digest(); - function customTemplateFn(routePathParams) { - customTemplateWatcher(routePathParams); - expect(routePathParams).toEqual({id: 'id3'}); - return '

          ' + routePathParams.id + '

          '; - } + expect(firstController).not.toHaveBeenCalled(); + expect(firstTemplate).not.toHaveBeenCalled(); + expect(firstResolve).not.toHaveBeenCalled(); - module(function($routeProvider) { - $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); - $routeProvider.when('/foo/:id', {template: customTemplateFn}); + expect(secondController).toHaveBeenCalled(); + expect(secondTemplate).toHaveBeenCalled(); + expect(secondResolve).toHaveBeenCalled(); + + dealoc(element); + }); }); - inject(function($route, $location, $rootScope) { - $location.path('/foo/id3'); - $rootScope.$digest(); - expect(customTemplateWatcher).toHaveBeenCalledWith({id: 'id3'}); + it('should not redirect transition if `redirectTo` returns `undefined`', function() { + var controller = jasmine.createSpy('first controller spy'); + var templateFn = jasmine.createSpy('first template spy').and.returnValue('redirected view'); + module(function($routeProvider) { + $routeProvider.when('/redirect/to/undefined', { + template: templateFn, + redirectTo: function() {}, + controller: controller + }); + }); + inject(function($route, $location, $rootScope, $compile) { + var element = $compile('
          ')($rootScope); + $location.path('/redirect/to/undefined'); + $rootScope.$digest(); + expect(controller).toHaveBeenCalled(); + expect(templateFn).toHaveBeenCalled(); + expect($location.path()).toEqual('/redirect/to/undefined'); + dealoc(element); + }); }); }); + describe('via `resolveRedirectTo`', function() { + var $compile; + var $location; + var $rootScope; + var $route; - it('should allow using a function as a templateUrl', function() { - var customTemplateUrlWatcher = jasmine.createSpy('customTemplateUrlWatcher'); + beforeEach(module(function() { + return function(_$compile_, _$location_, _$rootScope_, _$route_) { + $compile = _$compile_; + $location = _$location_; + $rootScope = _$rootScope_; + $route = _$route_; + }; + })); - function customTemplateUrlFn(routePathParams) { - customTemplateUrlWatcher(routePathParams); - expect(routePathParams).toEqual({id: 'id3'}); - return 'foo.html'; - } - module(function($routeProvider) { - $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); - $routeProvider.when('/foo/:id', {templateUrl: customTemplateUrlFn}); - }); + it('should be ignored if `redirectTo` is also present', function() { + var newUrl; + var getNewUrl = function() { return newUrl; }; - inject(function($route, $location, $rootScope) { - $location.path('/foo/id3'); - $rootScope.$digest(); + var resolveRedirectToSpy = jasmine.createSpy('resolveRedirectTo').and.returnValue('/bar'); + var redirectToSpy = jasmine.createSpy('redirectTo').and.callFake(getNewUrl); + var templateSpy = jasmine.createSpy('template').and.returnValue('Foo'); - expect(customTemplateUrlWatcher).toHaveBeenCalledWith({id: 'id3'}); - expect($route.current.loadedTemplateUrl).toEqual('foo.html'); + module(function($routeProvider) { + $routeProvider. + when('/foo', { + resolveRedirectTo: resolveRedirectToSpy, + redirectTo: redirectToSpy, + template: templateSpy + }). + when('/bar', {template: 'Bar'}). + when('/baz', {template: 'Baz'}); + }); + + inject(function() { + newUrl = '/baz'; + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/baz'); + expect($route.current.template).toBe('Baz'); + expect(resolveRedirectToSpy).not.toHaveBeenCalled(); + expect(redirectToSpy).toHaveBeenCalled(); + expect(templateSpy).not.toHaveBeenCalled(); + + redirectToSpy.calls.reset(); + + newUrl = undefined; + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/foo'); + expect($route.current.template).toBe(templateSpy); + expect(resolveRedirectToSpy).not.toHaveBeenCalled(); + expect(redirectToSpy).toHaveBeenCalled(); + expect(templateSpy).toHaveBeenCalled(); + }); + }); + + + it('should redirect to the returned url', function() { + module(function($routeProvider) { + $routeProvider. + when('/foo', {resolveRedirectTo: function() { return '/bar?baz=qux'; }}). + when('/bar', {template: 'Bar'}); + }); + + inject(function() { + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/bar'); + expect($location.search()).toEqual({baz: 'qux'}); + expect($route.current.template).toBe('Bar'); + }); + }); + + + it('should support returning a promise', function() { + module(function($routeProvider) { + $routeProvider. + when('/foo', {resolveRedirectTo: function($q) { return $q.resolve('/bar'); }}). + when('/bar', {template: 'Bar'}); + }); + + inject(function() { + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/bar'); + expect($route.current.template).toBe('Bar'); + }); + }); + + + it('should support dependency injection', function() { + module(function($provide, $routeProvider) { + $provide.value('nextRoute', '/bar'); + + $routeProvider. + when('/foo', { + resolveRedirectTo: function(nextRoute) { + return nextRoute; + } + }); + }); + + inject(function() { + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/bar'); + }); + }); + + + it('should have access to the current routeParams via `$route.current.params`', function() { + module(function($routeProvider) { + $routeProvider. + when('/foo/:bar/baz/:qux', { + resolveRedirectTo: function($route) { + expect($route.current.params).toEqual(jasmine.objectContaining({ + bar: '1', + qux: '2' + })); + + return '/passed'; + } + }); + }); + + inject(function() { + $location.path('/foo/1/baz/2').search({bar: 'qux'}); + $rootScope.$digest(); + + expect($location.path()).toBe('/passed'); + }); + }); + + + it('should not process route bits until the promise is resolved', function() { + var spies = createSpies(); + var called = false; + var deferred; + + module(function($routeProvider) { + setupRoutes($routeProvider, spies, function($q) { + called = true; + deferred = $q.defer(); + return deferred.promise; + }); + }); + + inject(function() { + var element = $compile('
          ')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/foo'); + expect(called).toBe(true); + expect(spies.fooResolveSpy).not.toHaveBeenCalled(); + expect(spies.fooTemplateSpy).not.toHaveBeenCalled(); + expect(spies.fooControllerSpy).not.toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + + deferred.resolve('/bar'); + $rootScope.$digest(); + expect($location.path()).toBe('/bar'); + expect(spies.fooResolveSpy).not.toHaveBeenCalled(); + expect(spies.fooTemplateSpy).not.toHaveBeenCalled(); + expect(spies.fooControllerSpy).not.toHaveBeenCalled(); + expect(spies.barResolveSpy).toHaveBeenCalled(); + expect(spies.barTemplateSpy).toHaveBeenCalled(); + expect(spies.barControllerSpy).toHaveBeenCalled(); + + dealoc(element); + }); + }); + + + it('should not redirect if `undefined` is returned', function() { + var spies = createSpies(); + var called = false; + + module(function($routeProvider) { + setupRoutes($routeProvider, spies, function() { + called = true; + return undefined; + }); + }); + + inject(function() { + var element = $compile('
          ')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/foo'); + expect(called).toBe(true); + expect(spies.fooResolveSpy).toHaveBeenCalled(); + expect(spies.fooTemplateSpy).toHaveBeenCalled(); + expect(spies.fooControllerSpy).toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + + dealoc(element); + }); + }); + + + it('should not redirect if the returned promise resolves to `undefined`', function() { + var spies = createSpies(); + var called = false; + + module(function($routeProvider) { + setupRoutes($routeProvider, spies, function($q) { + called = true; + return $q.resolve(undefined); + }); + }); + + inject(function() { + var element = $compile('
          ')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/foo'); + expect(called).toBe(true); + expect(spies.fooResolveSpy).toHaveBeenCalled(); + expect(spies.fooTemplateSpy).toHaveBeenCalled(); + expect(spies.fooControllerSpy).toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + + dealoc(element); + }); + }); + + + it('should not redirect if the returned promise gets rejected', function() { + var spies = createSpies(); + var called = false; + + module(function($routeProvider) { + setupRoutes($routeProvider, spies, function($q) { + called = true; + return $q.reject(''); + }); + }); + + inject(function() { + spyOn($rootScope, '$broadcast').and.callThrough(); + + var element = $compile('
          ')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/foo'); + expect(called).toBe(true); + expect(spies.fooResolveSpy).not.toHaveBeenCalled(); + expect(spies.fooTemplateSpy).not.toHaveBeenCalled(); + expect(spies.fooControllerSpy).not.toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + + var lastCallArgs = $rootScope.$broadcast.calls.mostRecent().args; + expect(lastCallArgs[0]).toBe('$routeChangeError'); + + dealoc(element); + }); + }); + + + it('should ignore previous redirection if newer transition happened', function() { + var spies = createSpies(); + var called = false; + var deferred; + + module(function($routeProvider) { + setupRoutes($routeProvider, spies, function($q) { + called = true; + deferred = $q.defer(); + return deferred.promise; + }); + }); + + inject(function() { + spyOn($location, 'url').and.callThrough(); + + var element = $compile('
          ')($rootScope); + + $location.path('/foo'); + $rootScope.$digest(); + + expect($location.path()).toBe('/foo'); + expect(called).toBe(true); + expect(spies.fooResolveSpy).not.toHaveBeenCalled(); + expect(spies.fooTemplateSpy).not.toHaveBeenCalled(); + expect(spies.fooControllerSpy).not.toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + expect(spies.bazResolveSpy).not.toHaveBeenCalled(); + expect(spies.bazTemplateSpy).not.toHaveBeenCalled(); + expect(spies.bazControllerSpy).not.toHaveBeenCalled(); + + $location.path('/baz'); + $rootScope.$digest(); + + expect($location.path()).toBe('/baz'); + expect(spies.fooResolveSpy).not.toHaveBeenCalled(); + expect(spies.fooTemplateSpy).not.toHaveBeenCalled(); + expect(spies.fooControllerSpy).not.toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + expect(spies.bazResolveSpy).toHaveBeenCalledOnce(); + expect(spies.bazTemplateSpy).toHaveBeenCalledOnce(); + expect(spies.bazControllerSpy).toHaveBeenCalledOnce(); + + deferred.resolve(); + $rootScope.$digest(); + + expect($location.path()).toBe('/baz'); + expect(spies.fooResolveSpy).not.toHaveBeenCalled(); + expect(spies.fooTemplateSpy).not.toHaveBeenCalled(); + expect(spies.fooControllerSpy).not.toHaveBeenCalled(); + expect(spies.barResolveSpy).not.toHaveBeenCalled(); + expect(spies.barTemplateSpy).not.toHaveBeenCalled(); + expect(spies.barControllerSpy).not.toHaveBeenCalled(); + expect(spies.bazResolveSpy).toHaveBeenCalledOnce(); + expect(spies.bazTemplateSpy).toHaveBeenCalledOnce(); + expect(spies.bazControllerSpy).toHaveBeenCalledOnce(); + + dealoc(element); + }); + }); + + + // Helpers + function createSpies() { + return { + fooResolveSpy: jasmine.createSpy('fooResolve'), + fooTemplateSpy: jasmine.createSpy('fooTemplate').and.returnValue('Foo'), + fooControllerSpy: jasmine.createSpy('fooController'), + barResolveSpy: jasmine.createSpy('barResolve'), + barTemplateSpy: jasmine.createSpy('barTemplate').and.returnValue('Bar'), + barControllerSpy: jasmine.createSpy('barController'), + bazResolveSpy: jasmine.createSpy('bazResolve'), + bazTemplateSpy: jasmine.createSpy('bazTemplate').and.returnValue('Baz'), + bazControllerSpy: jasmine.createSpy('bazController') + }; + } + + function setupRoutes(routeProvider, spies, resolveRedirectToFn) { + routeProvider. + when('/foo', { + resolveRedirectTo: resolveRedirectToFn, + resolve: {_: spies.fooResolveSpy}, + template: spies.fooTemplateSpy, + controller: spies.fooControllerSpy + }). + when('/bar', { + resolve: {_: spies.barResolveSpy}, + template: spies.barTemplateSpy, + controller: spies.barControllerSpy + }). + when('/baz', { + resolve: {_: spies.bazResolveSpy}, + template: spies.bazTemplateSpy, + controller: spies.bazControllerSpy + }); + } + }); + }); + + + describe('reloadOnUrl', function() { + it('should reload when `reloadOnUrl` is true and `.url()` changes', function() { + var routeChange = jasmine.createSpy('routeChange'); + + module(function($routeProvider) { + $routeProvider.when('/path/:param', {}); + }); + + inject(function($location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeStart', routeChange); + + // Initial load + $location.path('/path/foo'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({param: 'foo'}); + + routeChange.calls.reset(); + + // Reload on `path` change + $location.path('/path/bar'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({param: 'bar'}); + + routeChange.calls.reset(); + + // Reload on `search` change + $location.search('foo', 'bar'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({param: 'bar', foo: 'bar'}); + + routeChange.calls.reset(); + + // Reload on `hash` change + $location.hash('baz'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({param: 'bar', foo: 'bar'}); }); }); - describe('reload', function() { - it('should reload even if reloadOnSearch is false', function() { - var routeChangeSpy = jasmine.createSpy('route change'); + it('should reload when `reloadOnUrl` is false and URL maps to different route', + function() { + var routeChange = jasmine.createSpy('routeChange'); + var routeUpdate = jasmine.createSpy('routeUpdate'); module(function($routeProvider) { - $routeProvider.when('/bar/:barId', {controller: angular.noop, reloadOnSearch: false}); + $routeProvider. + when('/path/:param', {reloadOnUrl: false}). + otherwise({}); }); - inject(function($route, $location, $rootScope, $routeParams) { - $rootScope.$on('$routeChangeSuccess', routeChangeSpy); + inject(function($location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeStart', routeChange); + $rootScope.$on('$routeChangeSuccess', routeChange); + $rootScope.$on('$routeUpdate', routeUpdate); - $location.path('/bar/123'); + expect(routeChange).not.toHaveBeenCalled(); + + // Initial load + $location.path('/path/foo'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledTimes(2); + expect(routeUpdate).not.toHaveBeenCalled(); + expect($routeParams).toEqual({param: 'foo'}); + + routeChange.calls.reset(); + + // Route change + $location.path('/other/path/bar'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledTimes(2); + expect(routeUpdate).not.toHaveBeenCalled(); + expect($routeParams).toEqual({}); + }); + } + ); + + + it('should not reload when `reloadOnUrl` is false and URL maps to the same route', + function() { + var routeChange = jasmine.createSpy('routeChange'); + var routeUpdate = jasmine.createSpy('routeUpdate'); + + module(function($routeProvider) { + $routeProvider.when('/path/:param', {reloadOnUrl: false}); + }); + + inject(function($location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeStart', routeChange); + $rootScope.$on('$routeChangeSuccess', routeChange); + $rootScope.$on('$routeUpdate', routeUpdate); + + expect(routeChange).not.toHaveBeenCalled(); + + // Initial load + $location.path('/path/foo'); $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123'}); - expect(routeChangeSpy).toHaveBeenCalledOnce(); - routeChangeSpy.reset(); + expect(routeChange).toHaveBeenCalledTimes(2); + expect(routeUpdate).not.toHaveBeenCalled(); + expect($routeParams).toEqual({param: 'foo'}); + + routeChange.calls.reset(); - $location.path('/bar/123').search('a=b'); + // Route update (no reload) + $location.path('/path/bar').search('foo', 'bar').hash('baz'); $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123', a:'b'}); - expect(routeChangeSpy).not.toHaveBeenCalled(); + expect(routeChange).not.toHaveBeenCalled(); + expect(routeUpdate).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({param: 'bar', foo: 'bar'}); + }); + } + ); + + + it('should update `$routeParams` even when not reloading a route', function() { + var routeChange = jasmine.createSpy('routeChange'); + + module(function($routeProvider) { + $routeProvider.when('/path/:param', {reloadOnUrl: false}); + }); + + inject(function($location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeStart', routeChange); + $rootScope.$on('$routeChangeSuccess', routeChange); + + expect(routeChange).not.toHaveBeenCalled(); + + // Initial load + $location.path('/path/foo'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledTimes(2); + expect($routeParams).toEqual({param: 'foo'}); + + routeChange.calls.reset(); + + // Route update (no reload) + $location.path('/path/bar'); + $rootScope.$digest(); + expect(routeChange).not.toHaveBeenCalled(); + expect($routeParams).toEqual({param: 'bar'}); + }); + }); + + + describe('with `$route.reload()`', function() { + var $location; + var $log; + var $rootScope; + var $route; + var routeChangeStart; + var routeChangeSuccess; + + beforeEach(module(function($routeProvider) { + $routeProvider.when('/path/:param', { + template: '', + reloadOnUrl: false, + controller: function Controller($log) { + $log.debug('initialized'); + } + }); + })); + + beforeEach(inject(function($compile, _$location_, _$log_, _$rootScope_, _$route_) { + $location = _$location_; + $log = _$log_; + $rootScope = _$rootScope_; + $route = _$route_; + + routeChangeStart = jasmine.createSpy('routeChangeStart'); + routeChangeSuccess = jasmine.createSpy('routeChangeSuccess'); + + $rootScope.$on('$routeChangeStart', routeChangeStart); + $rootScope.$on('$routeChangeSuccess', routeChangeSuccess); + + element = $compile('
          ')($rootScope); + })); + + + it('should reload the current route', function() { + $location.path('/path/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/path/foo'); + expect(routeChangeStart).toHaveBeenCalledOnce(); + expect(routeChangeSuccess).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + routeChangeStart.calls.reset(); + routeChangeSuccess.calls.reset(); + $log.reset(); + + $route.reload(); + $rootScope.$digest(); + expect($location.path()).toBe('/path/foo'); + expect(routeChangeStart).toHaveBeenCalledOnce(); + expect(routeChangeSuccess).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + $log.reset(); + }); + + + it('should support preventing a route reload', function() { + $location.path('/path/foo'); + $rootScope.$digest(); + expect($location.path()).toBe('/path/foo'); + expect(routeChangeStart).toHaveBeenCalledOnce(); + expect(routeChangeSuccess).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + routeChangeStart.calls.reset(); + routeChangeSuccess.calls.reset(); + $log.reset(); + + routeChangeStart.and.callFake(function(evt) { evt.preventDefault(); }); + + $route.reload(); + $rootScope.$digest(); + expect($location.path()).toBe('/path/foo'); + expect(routeChangeStart).toHaveBeenCalledOnce(); + expect(routeChangeSuccess).not.toHaveBeenCalled(); + expect($log.debug.logs).toEqual([]); + }); + + + it('should reload the current route even if `reloadOnUrl` is disabled', + inject(function($routeParams) { + $location.path('/path/foo'); + $rootScope.$digest(); + expect(routeChangeStart).toHaveBeenCalledOnce(); + expect(routeChangeSuccess).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + expect($routeParams).toEqual({param: 'foo'}); + + routeChangeStart.calls.reset(); + routeChangeSuccess.calls.reset(); + $log.reset(); + + $location.path('/path/bar'); + $rootScope.$digest(); + expect(routeChangeStart).not.toHaveBeenCalled(); + expect(routeChangeSuccess).not.toHaveBeenCalled(); + expect($log.debug.logs).toEqual([]); + expect($routeParams).toEqual({param: 'bar'}); $route.reload(); $rootScope.$digest(); - expect($routeParams).toEqual({barId:'123', a:'b'}); - expect(routeChangeSpy).toHaveBeenCalledOnce(); + expect(routeChangeStart).toHaveBeenCalledOnce(); + expect(routeChangeSuccess).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + expect($routeParams).toEqual({param: 'bar'}); + + $log.reset(); + }) + ); + }); + }); + + describe('reloadOnSearch', function() { + it('should not have any effect if `reloadOnUrl` is false', function() { + var reloaded = jasmine.createSpy('route reload'); + + module(function($routeProvider) { + $routeProvider.when('/foo', { + reloadOnUrl: false, + reloadOnSearch: true + }); + }); + + inject(function($route, $location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeStart', reloaded); + + $location.path('/foo'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({}); + + reloaded.calls.reset(); + + // trigger reload (via .search()) + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(reloaded).not.toHaveBeenCalled(); + expect($routeParams).toEqual({foo: 'bar'}); + + // trigger reload (via .hash()) + $location.hash('baz'); + $rootScope.$digest(); + expect(reloaded).not.toHaveBeenCalled(); + expect($routeParams).toEqual({foo: 'bar'}); + }); + }); + + + it('should reload when `reloadOnSearch` is true and `.search()`/`.hash()` changes', + function() { + var reloaded = jasmine.createSpy('route reload'); + + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: angular.noop}); + }); + + inject(function($route, $location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeStart', reloaded); + + $location.path('/foo'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({}); + + reloaded.calls.reset(); + + // trigger reload (via .search()) + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({foo: 'bar'}); + + reloaded.calls.reset(); + + // trigger reload (via .hash()) + $location.hash('baz'); + $rootScope.$digest(); + expect(reloaded).toHaveBeenCalledOnce(); + expect($routeParams).toEqual({foo: 'bar'}); + }); + } + ); + + + it('should not reload when `reloadOnSearch` is false and `.search()`/`.hash()` changes', + function() { + var routeChange = jasmine.createSpy('route change'), + routeUpdate = jasmine.createSpy('route update'); + + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: angular.noop, reloadOnSearch: false}); + }); + + inject(function($route, $location, $rootScope) { + $rootScope.$on('$routeChangeStart', routeChange); + $rootScope.$on('$routeChangeSuccess', routeChange); + $rootScope.$on('$routeUpdate', routeUpdate); + + expect(routeChange).not.toHaveBeenCalled(); + + $location.path('/foo'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledTimes(2); + expect(routeUpdate).not.toHaveBeenCalled(); + + routeChange.calls.reset(); + + // don't trigger reload (via .search()) + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(routeChange).not.toHaveBeenCalled(); + expect(routeUpdate).toHaveBeenCalledOnce(); + + routeUpdate.calls.reset(); + + // don't trigger reload (via .hash()) + $location.hash('baz'); + $rootScope.$digest(); + expect(routeChange).not.toHaveBeenCalled(); + expect(routeUpdate).toHaveBeenCalled(); + }); + } + ); + + + it('should reload when `reloadOnSearch` is false and url differs only in route path param', + function() { + var routeChange = jasmine.createSpy('route change'); + + module(function($routeProvider) { + $routeProvider.when('/foo/:fooId', {controller: angular.noop, reloadOnSearch: false}); + }); + + inject(function($route, $location, $rootScope) { + $rootScope.$on('$routeChangeStart', routeChange); + $rootScope.$on('$routeChangeSuccess', routeChange); + + expect(routeChange).not.toHaveBeenCalled(); + + $location.path('/foo/aaa'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledTimes(2); + routeChange.calls.reset(); + + $location.path('/foo/bbb'); + $rootScope.$digest(); + expect(routeChange).toHaveBeenCalledTimes(2); + routeChange.calls.reset(); + + $location.search({foo: 'bar'}).hash('baz'); + $rootScope.$digest(); + expect(routeChange).not.toHaveBeenCalled(); }); + } + ); + + + it('should update params when `reloadOnSearch` is false and `.search()` changes', function() { + var routeParamsWatcher = jasmine.createSpy('routeParamsWatcher'); + + module(function($routeProvider) { + $routeProvider.when('/foo', {controller: angular.noop}); + $routeProvider.when('/bar/:barId', {controller: angular.noop, reloadOnSearch: false}); + }); + + inject(function($route, $location, $rootScope, $routeParams) { + $rootScope.$watch(function() { + return $routeParams; + }, function(value) { + routeParamsWatcher(value); + }, true); + + expect(routeParamsWatcher).not.toHaveBeenCalled(); + + $location.path('/foo'); + $rootScope.$digest(); + expect(routeParamsWatcher).toHaveBeenCalledWith({}); + routeParamsWatcher.calls.reset(); + + // trigger reload + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(routeParamsWatcher).toHaveBeenCalledWith({foo: 'bar'}); + routeParamsWatcher.calls.reset(); + + $location.path('/bar/123').search({}); + $rootScope.$digest(); + expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123'}); + routeParamsWatcher.calls.reset(); + + // don't trigger reload + $location.search({foo: 'bar'}); + $rootScope.$digest(); + expect(routeParamsWatcher).toHaveBeenCalledWith({barId: '123', foo: 'bar'}); + }); + }); + + + it('should allow using a function as a template', function() { + var customTemplateWatcher = jasmine.createSpy('customTemplateWatcher'); + + function customTemplateFn(routePathParams) { + customTemplateWatcher(routePathParams); + expect(routePathParams).toEqual({id: 'id3'}); + return '

          ' + routePathParams.id + '

          '; + } + + module(function($routeProvider) { + $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); + $routeProvider.when('/foo/:id', {template: customTemplateFn}); + }); + + inject(function($route, $location, $rootScope) { + $location.path('/foo/id3'); + $rootScope.$digest(); + + expect(customTemplateWatcher).toHaveBeenCalledWith({id: 'id3'}); + }); + }); + + + it('should allow using a function as a templateUrl', function() { + var customTemplateUrlWatcher = jasmine.createSpy('customTemplateUrlWatcher'); + + function customTemplateUrlFn(routePathParams) { + customTemplateUrlWatcher(routePathParams); + expect(routePathParams).toEqual({id: 'id3'}); + return 'foo.html'; + } + + module(function($routeProvider) { + $routeProvider.when('/bar/:id/:subid/:subsubid', {templateUrl: 'bar.html'}); + $routeProvider.when('/foo/:id', {templateUrl: customTemplateUrlFn}); + }); + + inject(function($route, $location, $rootScope) { + $location.path('/foo/id3'); + $rootScope.$digest(); + + expect(customTemplateUrlWatcher).toHaveBeenCalledWith({id: 'id3'}); + expect($route.current.loadedTemplateUrl).toEqual('foo.html'); + }); + }); + + + describe('with `$route.reload()`', function() { + var $location; + var $log; + var $rootScope; + var $route; + var routeChangeStartSpy; + var routeChangeSuccessSpy; + + beforeEach(module(function($routeProvider) { + $routeProvider.when('/bar/:barId', { + template: '', + controller: controller, + reloadOnSearch: false + }); + + function controller($log) { + $log.debug('initialized'); + } + })); + beforeEach(inject(function($compile, _$location_, _$log_, _$rootScope_, _$route_) { + $location = _$location_; + $log = _$log_; + $rootScope = _$rootScope_; + $route = _$route_; + + routeChangeStartSpy = jasmine.createSpy('routeChangeStart'); + routeChangeSuccessSpy = jasmine.createSpy('routeChangeSuccess'); + + $rootScope.$on('$routeChangeStart', routeChangeStartSpy); + $rootScope.$on('$routeChangeSuccess', routeChangeSuccessSpy); + + element = $compile('
          ')($rootScope); + })); + + + it('should reload the current route', function() { + $location.path('/bar/123'); + $rootScope.$digest(); + expect($location.path()).toBe('/bar/123'); + expect(routeChangeStartSpy).toHaveBeenCalledOnce(); + expect(routeChangeSuccessSpy).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + routeChangeStartSpy.calls.reset(); + routeChangeSuccessSpy.calls.reset(); + $log.reset(); + + $route.reload(); + $rootScope.$digest(); + expect($location.path()).toBe('/bar/123'); + expect(routeChangeStartSpy).toHaveBeenCalledOnce(); + expect(routeChangeSuccessSpy).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + $log.reset(); + }); + + + it('should support preventing a route reload', function() { + $location.path('/bar/123'); + $rootScope.$digest(); + expect($location.path()).toBe('/bar/123'); + expect(routeChangeStartSpy).toHaveBeenCalledOnce(); + expect(routeChangeSuccessSpy).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + routeChangeStartSpy.calls.reset(); + routeChangeSuccessSpy.calls.reset(); + $log.reset(); + + routeChangeStartSpy.and.callFake(function(evt) { evt.preventDefault(); }); + + $route.reload(); + $rootScope.$digest(); + expect($location.path()).toBe('/bar/123'); + expect(routeChangeStartSpy).toHaveBeenCalledOnce(); + expect(routeChangeSuccessSpy).not.toHaveBeenCalled(); + expect($log.debug.logs).toEqual([]); }); + + + it('should reload even if reloadOnSearch is false', inject(function($routeParams) { + $location.path('/bar/123'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId: '123'}); + expect(routeChangeSuccessSpy).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + routeChangeSuccessSpy.calls.reset(); + $log.reset(); + + $location.search('a=b'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId: '123', a: 'b'}); + expect(routeChangeSuccessSpy).not.toHaveBeenCalled(); + expect($log.debug.logs).toEqual([]); + + routeChangeSuccessSpy.calls.reset(); + $log.reset(); + + $location.hash('c'); + $rootScope.$digest(); + expect($routeParams).toEqual({barId: '123', a: 'b'}); + expect(routeChangeSuccessSpy).not.toHaveBeenCalled(); + expect($log.debug.logs).toEqual([]); + + $route.reload(); + $rootScope.$digest(); + expect($routeParams).toEqual({barId: '123', a: 'b'}); + expect(routeChangeSuccessSpy).toHaveBeenCalledOnce(); + expect($log.debug.logs).toEqual([['initialized']]); + + $log.reset(); + })); }); }); @@ -1258,7 +2302,7 @@ describe('$route', function() { $location.path('/bar/1'); $rootScope.$digest(); - routeChangeSpy.reset(); + routeChangeSpy.calls.reset(); $route.updateParams({barId: '2'}); $rootScope.$digest(); @@ -1281,7 +2325,7 @@ describe('$route', function() { $location.path('/bar/1/2/3/4'); $rootScope.$digest(); - routeChangeSpy.reset(); + routeChangeSpy.calls.reset(); $route.updateParams({barId: '5', fooId: '6', spamId: '7', eggId: '8'}); $rootScope.$digest(); @@ -1304,7 +2348,7 @@ describe('$route', function() { $location.path('/bar/1/2/3/4'); $rootScope.$digest(); - routeChangeSpy.reset(); + routeChangeSpy.calls.reset(); $route.updateParams({barId: '5', fooId: '6'}); $rootScope.$digest(); @@ -1329,7 +2373,7 @@ describe('$route', function() { $location.path('/bar/1/2/3'); $location.search({initial: 'true'}); $rootScope.$digest(); - routeChangeSpy.reset(); + routeChangeSpy.calls.reset(); $route.updateParams({barId: '5', fooId: '6', eggId: '4'}); $rootScope.$digest(); @@ -1341,9 +2385,196 @@ describe('$route', function() { }); }); + it('should not update query params when an optional property was previously not in path', function() { + var routeChangeSpy = jasmine.createSpy('route change'); + + module(function($routeProvider) { + $routeProvider.when('/bar/:barId/:fooId/:spamId/:eggId?', {controller: angular.noop}); + }); + + inject(function($route, $routeParams, $location, $rootScope) { + $rootScope.$on('$routeChangeSuccess', routeChangeSpy); + + $location.path('/bar/1/2/3'); + $location.search({initial: 'true'}); + $rootScope.$digest(); + routeChangeSpy.calls.reset(); + + $route.updateParams({barId: '5', fooId: '6', eggId: '4'}); + $rootScope.$digest(); + + expect($routeParams).toEqual({barId: '5', fooId: '6', spamId: '3', eggId: '4', initial: 'true'}); + expect(routeChangeSpy).toHaveBeenCalledOnce(); + expect($location.path()).toEqual('/bar/5/6/3/4'); + expect($location.search()).toEqual({initial: 'true'}); + }); + }); it('should complain if called without an existing route', inject(function($route) { - expect($route.updateParams).toThrowMinErr('ngRoute', 'norout'); + expect(function() { $route.updateParams(); }).toThrowMinErr('ngRoute', 'norout'); })); }); + + describe('testability', function() { + it('should wait for $resolve promises before calling callbacks', function() { + var deferred; + + module(function($routeProvider) { + $routeProvider.when('/path', { + resolve: { + a: function($q) { + deferred = $q.defer(); + return deferred.promise; + } + } + }); + }); + + inject(function($browser, $location, $rootScope, $$testability) { + $location.path('/path'); + $rootScope.$digest(); + + var callback = jasmine.createSpy('callback'); + $$testability.whenStable(callback); + expect(callback).not.toHaveBeenCalled(); + + deferred.resolve(); + $browser.defer.flush(); + expect(callback).toHaveBeenCalled(); + }); + }); + + it('should call callback after $resolve promises are rejected', function() { + var deferred; + + module(function($routeProvider) { + $routeProvider.when('/path', { + resolve: { + a: function($q) { + deferred = $q.defer(); + return deferred.promise; + } + } + }); + }); + + inject(function($browser, $location, $rootScope, $$testability) { + $location.path('/path'); + $rootScope.$digest(); + + var callback = jasmine.createSpy('callback'); + $$testability.whenStable(callback); + expect(callback).not.toHaveBeenCalled(); + + deferred.reject(); + $browser.defer.flush(); + expect(callback).toHaveBeenCalled(); + }); + }); + + it('should wait for resolveRedirectTo promises before calling callbacks', function() { + var deferred; + + module(function($routeProvider) { + $routeProvider.when('/path', { + resolveRedirectTo: function($q) { + deferred = $q.defer(); + return deferred.promise; + } + }); + }); + + inject(function($browser, $location, $rootScope, $$testability) { + $location.path('/path'); + $rootScope.$digest(); + + var callback = jasmine.createSpy('callback'); + $$testability.whenStable(callback); + expect(callback).not.toHaveBeenCalled(); + + deferred.resolve(); + $browser.defer.flush(); + expect(callback).toHaveBeenCalled(); + }); + }); + + it('should call callback after resolveRedirectTo promises are rejected', function() { + var deferred; + + module(function($routeProvider) { + $routeProvider.when('/path', { + resolveRedirectTo: function($q) { + deferred = $q.defer(); + return deferred.promise; + } + }); + }); + + inject(function($browser, $location, $rootScope, $$testability) { + $location.path('/path'); + $rootScope.$digest(); + + var callback = jasmine.createSpy('callback'); + $$testability.whenStable(callback); + expect(callback).not.toHaveBeenCalled(); + + deferred.reject(); + $browser.defer.flush(); + expect(callback).toHaveBeenCalled(); + }); + }); + + it('should wait for all route promises before calling callbacks', function() { + var deferreds = {}; + + module(function($routeProvider) { + addRouteWithAsyncRedirect('/foo', '/bar'); + addRouteWithAsyncRedirect('/bar', '/baz'); + addRouteWithAsyncRedirect('/baz', '/qux'); + $routeProvider.when('/qux', { + resolve: { + a: function($q) { + var deferred = deferreds['/qux'] = $q.defer(); + return deferred.promise; + } + } + }); + + // Helpers + function addRouteWithAsyncRedirect(fromPath, toPath) { + $routeProvider.when(fromPath, { + resolveRedirectTo: function($q) { + var deferred = deferreds[fromPath] = $q.defer(); + return deferred.promise.then(function() { return toPath; }); + } + }); + } + }); + + inject(function($browser, $location, $rootScope, $$testability) { + $location.path('/foo'); + $rootScope.$digest(); + + var callback = jasmine.createSpy('callback'); + $$testability.whenStable(callback); + expect(callback).not.toHaveBeenCalled(); + + deferreds['/foo'].resolve(); + $browser.defer.flush(); + expect(callback).not.toHaveBeenCalled(); + + deferreds['/bar'].resolve(); + $browser.defer.flush(); + expect(callback).not.toHaveBeenCalled(); + + deferreds['/baz'].resolve(); + $browser.defer.flush(); + expect(callback).not.toHaveBeenCalled(); + + deferreds['/qux'].resolve(); + $browser.defer.flush(); + expect(callback).toHaveBeenCalled(); + }); + }); + }); }); diff --git a/test/ngSanitize/directive/ngBindHtmlSpec.js b/test/ngSanitize/directive/ngBindHtmlSpec.js index 6759c6d20fcd..74eb0f231baa 100644 --- a/test/ngSanitize/directive/ngBindHtmlSpec.js +++ b/test/ngSanitize/directive/ngBindHtmlSpec.js @@ -8,7 +8,7 @@ describe('ngBindHtml', function() { var element = $compile('
          ')($rootScope); $rootScope.html = '
          hello
          '; $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual('
          hello
          '); + expect(lowercase(element.html())).toEqual('
          hello
          '); })); @@ -18,11 +18,11 @@ describe('ngBindHtml', function() { angular.forEach([null, undefined, ''], function(val) { $rootScope.html = 'some val'; $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual('some val'); + expect(lowercase(element.html())).toEqual('some val'); $rootScope.html = val; $rootScope.$digest(); - expect(angular.lowercase(element.html())).toEqual(''); + expect(lowercase(element.html())).toEqual(''); }); })); }); diff --git a/test/ngSanitize/filter/linkySpec.js b/test/ngSanitize/filter/linkySpec.js index baac45bd6d82..236766e61038 100644 --- a/test/ngSanitize/filter/linkySpec.js +++ b/test/ngSanitize/filter/linkySpec.js @@ -10,7 +10,7 @@ describe('linky', function() { })); it('should do basic filter', function() { - expect(linky("http://ab/ (http://a/) http://1.2/v:~-123. c “http://example.com” ‘http://me.com’")). + expect(linky('http://ab/ (http://a/) http://1.2/v:~-123. c “http://example.com” ‘http://me.com’')). toEqual('http://ab/ ' + '(
          http://a/) ' + '<http://a/> ' + @@ -20,18 +20,62 @@ describe('linky', function() { expect(linky(undefined)).not.toBeDefined(); }); + it('should return `undefined`/`null`/`""` values unchanged', function() { + expect(linky(undefined)).toBeUndefined(); + expect(linky(null)).toBe(null); + expect(linky('')).toBe(''); + }); + + it('should throw an error when used with a non-string value (other than `undefined`/`null`)', + function() { + expect(function() { linky(false); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: false'); + + expect(function() { linky(true); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: true'); + + expect(function() { linky(0); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: 0'); + + expect(function() { linky(42); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: 42'); + + expect(function() { linky({}); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: {}'); + + expect(function() { linky([]); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: []'); + + expect(function() { linky(noop); }). + toThrowMinErr('linky', 'notstring', 'Expected string but received: function noop()'); + } + ); + + it('should be case-insensitive', function() { + expect(linky('WWW.example.com')).toEqual('WWW.example.com'); + expect(linky('WWW.EXAMPLE.COM')).toEqual('WWW.EXAMPLE.COM'); + expect(linky('HTTP://www.example.com')).toEqual('HTTP://www.example.com'); + expect(linky('HTTP://example.com')).toEqual('HTTP://example.com'); + expect(linky('HTTPS://www.example.com')).toEqual('HTTPS://www.example.com'); + expect(linky('HTTPS://example.com')).toEqual('HTTPS://example.com'); + expect(linky('FTP://www.example.com')).toEqual('FTP://www.example.com'); + expect(linky('FTP://example.com')).toEqual('FTP://example.com'); + expect(linky('SFTP://www.example.com')).toEqual('SFTP://www.example.com'); + expect(linky('SFTP://example.com')).toEqual('SFTP://example.com'); + }); + it('should handle www.', function() { expect(linky('www.example.com')).toEqual('www.example.com'); }); it('should handle mailto:', function() { - expect(linky("mailto:me@example.com")). + expect(linky('mailto:me@example.com')). toEqual('me@example.com'); - expect(linky("me@example.com")). + expect(linky('me@example.com')). toEqual('me@example.com'); - expect(linky("send email to me@example.com, but")). + expect(linky('send email to me@example.com, but')). toEqual('send email to me@example.com, but'); - expect(linky("my email is \"me@example.com\"")). + expect(linky('my email is "me@example.com"')). toEqual('my email is "me@example.com"'); }); @@ -40,9 +84,57 @@ describe('linky', function() { }); it('should handle target:', function() { - expect(linky("http://example.com", "_blank")). - toEqual('http://example.com'); - expect(linky("http://example.com", "someNamedIFrame")). - toEqual('http://example.com'); + expect(linky('http://example.com', '_blank')). + toBeOneOf('http://example.com', + 'http://example.com'); + expect(linky('http://example.com', 'someNamedIFrame')). + toBeOneOf('http://example.com', + 'http://example.com'); + }); + + describe('custom attributes', function() { + + it('should optionally add custom attributes', function() { + expect(linky('http://example.com', '_self', {rel: 'nofollow'})). + toBeOneOf('http://example.com', + 'http://example.com'); + }); + + + it('should override target parameter with custom attributes', function() { + expect(linky('http://example.com', '_self', {target: '_blank'})). + toBeOneOf('http://example.com', + 'http://example.com'); + }); + + + it('should optionally add custom attributes from function', function() { + expect(linky('http://example.com', '_self', function(url) {return {'class': 'blue'};})). + toBeOneOf('http://example.com', + 'http://example.com', + 'http://example.com'); + }); + + + it('should pass url as parameter to custom attribute function', function() { + var linkParameters = jasmine.createSpy('linkParameters').and.returnValue({'class': 'blue'}); + linky('http://example.com', '_self', linkParameters); + expect(linkParameters).toHaveBeenCalledWith('http://example.com'); + }); + + + it('should call the attribute function for all links in the input', function() { + var attributeFn = jasmine.createSpy('attributeFn').and.returnValue({}); + linky('http://example.com and http://google.com', '_self', attributeFn); + expect(attributeFn.calls.allArgs()).toEqual([['http://example.com'], ['http://google.com']]); + }); + + + it('should strip unsafe attributes', function() { + expect(linky('http://example.com', '_self', {'class': 'blue', 'onclick': 'alert(\'Hi\')'})). + toBeOneOf('http://example.com', + 'http://example.com', + 'http://example.com'); + }); }); }); diff --git a/test/ngSanitize/sanitizeSpec.js b/test/ngSanitize/sanitizeSpec.js index 13db4a511ad6..ac3c44b3d59c 100644 --- a/test/ngSanitize/sanitizeSpec.js +++ b/test/ngSanitize/sanitizeSpec.js @@ -1,6 +1,8 @@ 'use strict'; describe('HTML', function() { + var ua = window.navigator.userAgent; + var isChrome = /Chrome/.test(ua) && !/Edge/.test(ua); var expectHTML; @@ -17,23 +19,25 @@ describe('HTML', function() { describe('htmlParser', function() { /* global htmlParser */ - if (angular.isUndefined(window.htmlParser)) return; var handler, start, text, comment; beforeEach(function() { - text = ""; + text = ''; + start = null; handler = { - start: function(tag, attrs, unary) { + start: function(tag, attrs) { start = { tag: tag, - attrs: attrs, - unary: unary + attrs: attrs }; // Since different browsers handle newlines differently we trim // so that it is easier to write tests. - angular.forEach(attrs, function(value, key) { + for (var i = 0, ii = attrs.length; i < ii; i++) { + var keyValue = attrs[i]; + var key = keyValue.key; + var value = keyValue.value; attrs[key] = value.replace(/^\s*/, '').replace(/\s*$/, ''); - }); + } }, chars: function(text_) { text += text_; @@ -45,40 +49,18 @@ describe('HTML', function() { comment = comment_; } }; + // Trigger the $sanitizer provider to execute, which initializes the `htmlParser` function. + inject(function($sanitize) {}); }); - it('should parse comments', function() { + it('should not parse comments', function() { htmlParser('', handler); - expect(comment).toEqual('FOOBAR'); - }); - - it('should throw an exception for invalid comments', function() { - var caught=false; - try { - htmlParser('', handler); - } - catch (ex) { - caught = true; - // expected an exception due to a bad parse - } - expect(caught).toBe(true); - }); - - it('double-dashes are not allowed in a comment', function() { - var caught=false; - try { - htmlParser('', handler); - } - catch (ex) { - caught = true; - // expected an exception due to a bad parse - } - expect(caught).toBe(true); + expect(comment).not.toBeDefined(); }); it('should parse basic format', function() { htmlParser('text', handler); - expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); + expect(start).toEqual({tag:'tag', attrs:{attr:'value'}}); expect(text).toEqual('text'); }); @@ -87,18 +69,6 @@ describe('HTML', function() { toBe('<- text1 text2 <1 text1 text2 <{'); }); - it('should throw badparse if text content contains "<" followed by "/" without matching ">"', function() { - expect(function() { - htmlParser('foo "', function() { - expect(function() { - htmlParser('foo 10 < 100

          ', handler); @@ -107,25 +77,25 @@ describe('HTML', function() { it('should parse newlines in tags', function() { htmlParser('text', handler); - expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); + expect(start).toEqual({tag:'tag', attrs:{attr:'value'}}); expect(text).toEqual('text'); }); it('should parse newlines in attributes', function() { htmlParser('text', handler); - expect(start).toEqual({tag:'tag', attrs:{attr:'value'}, unary:false}); + expect(start).toEqual({tag:'tag', attrs:{attr:'\nvalue\n'}}); expect(text).toEqual('text'); }); it('should parse namespace', function() { htmlParser('text', handler); - expect(start).toEqual({tag:'ns:t-a-g', attrs:{'ns:a-t-t-r':'value'}, unary:false}); + expect(start).toEqual({tag:'ns:t-a-g', attrs:{'ns:a-t-t-r':'\nvalue\n'}}); expect(text).toEqual('text'); }); it('should parse empty value attribute of node', function() { - htmlParser('', handler); - expect(start).toEqual({tag:'option', attrs:{selected:'', value:''}, unary:false}); + htmlParser('abc', handler); + expect(start).toEqual({tag:'test-foo', attrs:{selected:'', value:''}}); expect(text).toEqual('abc'); }); }); @@ -133,11 +103,17 @@ describe('HTML', function() { // THESE TESTS ARE EXECUTED WITH COMPILED ANGULAR it('should echo html', function() { expectHTML('helloworld.'). - toEqual('helloworld.'); + toBeOneOf('helloworld.', + 'helloworld.'); }); it('should remove script', function() { - expectHTML('ac.').toEqual('ac.'); + }); + + it('should remove script that has newline characters', function() { + expectHTML('a\n\revil\n\rc.').toEqual('ac.'); }); it('should remove DOCTYPE header', function() { @@ -156,16 +132,31 @@ describe('HTML', function() { expectHTML('a
          b
          c').toEqual('a
          b
          c'); }); + it('should handle large datasets', function() { + // Large is non-trivial to quantify, but handling ~100,000 should be sufficient for most purposes. + var largeNumber = 17; // 2^17 = 131,072 + var result = '
          b
          '; + // Ideally we would use repeat, but that isn't supported in IE. + for (var i = 0; i < largeNumber; i++) { + result += result; + } + expectHTML('a' + result + 'c').toEqual('a' + result + 'c'); + }); + it('should remove style', function() { expectHTML('ac.').toEqual('ac.'); }); + it('should remove style that has newline characters', function() { + expectHTML('ac.').toEqual('ac.'); }); it('should remove double nested script', function() { - expectHTML('ailc.').toEqual('ac.'); + expectHTML('ailc.').toEqual('ailc.'); }); it('should remove unknown names', function() { @@ -174,10 +165,11 @@ describe('HTML', function() { it('should remove unsafe value', function() { expectHTML('
          ').toEqual(''); + expectHTML('').toEqual(''); }); it('should handle self closed elements', function() { - expectHTML('a
          c').toEqual('a
          c'); + expectHTML('a
          c').toEqual('a
          c'); }); it('should handle namespace', function() { @@ -204,7 +196,8 @@ describe('HTML', function() { it('should ignore back slash as escape', function() { expectHTML('xxx\\'). - toEqual('xxx\\'); + toBeOneOf('xxx\\', + 'xxx\\'); }); it('should ignore object attributes', function() { @@ -238,36 +231,204 @@ describe('HTML', function() { expectHTML(false).toBe('false'); }); - it('should accept SVG tags', function() { - expectHTML('') - .toEqual(''); + + it('should strip svg elements if not enabled via provider', function() { + expectHTML('') + .toEqual(''); + }); + + it('should prevent mXSS attacks', function() { + expectHTML('CLICKME').toBe('CLICKME'); + }); + + it('should strip html comments', function() { + expectHTML('

          text1text2

          ') + .toEqual('

          text1text2

          '); }); - it('should sanitize SVG xlink:href attribute values', function() { - expectHTML('') - .toEqual(''); + describe('clobbered elements', function() { + + it('should throw on a form with an input named "parentNode"', function() { + inject(function($sanitize) { + + expect(function() { + $sanitize('
          '); + }).toThrowMinErr('$sanitize', 'elclob'); + + expect(function() { + $sanitize('
          '); + }).toThrowMinErr('$sanitize', 'elclob'); + }); + }); + + if (!/Edge\/\d{2,}/.test(window.navigator.userAgent)) { + // Skip test on Edge due to a browser bug. + it('should throw on a form with an input named "nextSibling"', function() { + inject(function($sanitize) { + + expect(function() { + $sanitize('
          '); + }).toThrowMinErr('$sanitize', 'elclob'); + + expect(function() { + $sanitize('
          '); + }).toThrowMinErr('$sanitize', 'elclob'); + + }); + }); + } + }); + + // See https://github.com/cure53/DOMPurify/blob/a992d3a75031cb8bb032e5ea8399ba972bdf9a65/src/purify.js#L439-L449 + it('should not allow JavaScript execution when creating inert document', inject(function($sanitize) { + $sanitize(''); + + expect(window.xxx).toBe(undefined); + delete window.xxx; + })); + + // See https://github.com/cure53/DOMPurify/releases/tag/0.6.7 + it('should not allow JavaScript hidden in badly formed HTML to get through sanitization (Firefox bug)', inject(function($sanitize) { + var doc = $sanitize('

          '); + expect(doc).toEqual('

          '); + })); + + describe('Custom white-list support', function() { + + var $sanitizeProvider; + beforeEach(module(function(_$sanitizeProvider_) { + $sanitizeProvider = _$sanitizeProvider_; + + $sanitizeProvider.addValidElements(['foo']); + $sanitizeProvider.addValidElements({ + htmlElements: ['foo-button', 'foo-video'], + htmlVoidElements: ['foo-input'], + svgElements: ['foo-svg'] + }); + $sanitizeProvider.addValidAttrs(['foo']); + })); + + it('should allow custom white-listed element', function() { + expectHTML('').toEqual(''); + expectHTML('').toEqual(''); + expectHTML('').toEqual(''); + }); + + it('should allow custom white-listed void element', function() { + expectHTML('').toEqual(''); + }); + + it('should allow custom white-listed void element to be used with closing tag', function() { + expectHTML('').toEqual(''); + }); - expectHTML('') - .toEqual(''); + it('should allow custom white-listed attribute', function() { + expectHTML('').toEqual(''); + }); + + it('should ignore custom white-listed SVG element if SVG disabled', function() { + expectHTML('').toEqual(''); + }); + + it('should not allow add custom element after service has been instantiated', inject(function($sanitize) { + $sanitizeProvider.addValidElements(['bar']); + expectHTML('').toEqual(''); + })); }); - it('should sanitize unknown namespaced SVG attributes', function() { - expectHTML('') - .toEqual(''); + describe('SVG support', function() { + + beforeEach(module(function($sanitizeProvider) { + $sanitizeProvider.enableSvg(true); + $sanitizeProvider.addValidElements({ + svgElements: ['font-face-uri'] + }); + })); + + it('should accept SVG tags', function() { + expectHTML('') + .toBeOneOf('', + '', + '', + ''); + }); + + it('should not ignore white-listed svg camelCased attributes', function() { + expectHTML('') + .toBeOneOf('', + ''); + + }); + + it('should allow custom white-listed SVG element', function() { + expectHTML('').toEqual(''); + }); + + it('should sanitize SVG xlink:href attribute values', function() { + expectHTML('') + .toBeOneOf('', + '', + ''); + + expectHTML('') + .toBeOneOf('', + '', + '', + ''); + }); + + it('should sanitize SVG xml:base attribute values', function() { + expectHTML('') + .toEqual(''); + + expectHTML('') + .toEqual(''); - expectHTML('') - .toEqual(''); + }); + + it('should sanitize unknown namespaced SVG attributes', function() { + expectHTML('') + .toBeOneOf('', + '', + ''); + + expectHTML('') + .toBeOneOf('', + '', + ''); + }); + + it('should not accept SVG animation tags', function() { + expectHTML('Click me') + .toBeOneOf('Click me', + 'Click me', + 'Click me'); + + expectHTML('' + + '') + .toBeOneOf('', + '', + '', + ''); + }); + + it('should not accept SVG `use` tags', function() { + expectHTML('') + .toBeOneOf('', + '', + ''); + }); }); + describe('htmlSanitizerWriter', function() { /* global htmlSanitizeWriter: false */ - if (angular.isUndefined(window.htmlSanitizeWriter)) return; var writer, html, uriValidator; beforeEach(function() { html = ''; uriValidator = jasmine.createSpy('uriValidator'); - writer = htmlSanitizeWriter({push:function(text) {html+=text;}}, uriValidator); + writer = htmlSanitizeWriter({push:function(text) {html += text;}}, uriValidator); }); it('should write basic HTML', function() { @@ -295,13 +456,13 @@ describe('HTML', function() { expect(html).toEqual('
          '); }); - it('should ignore missformed elements', function() { + it('should ignore misformed elements', function() { writer.start('d>i&v', {}); expect(html).toEqual(''); }); it('should ignore unknown attributes', function() { - writer.start('div', {unknown:""}); + writer.start('div', {unknown:''}); expect(html).toEqual('
          '); }); @@ -345,21 +506,21 @@ describe('HTML', function() { it('should call the uri validator', function() { writer.start('a', {href:'someUrl'}, false); expect(uriValidator).toHaveBeenCalledWith('someUrl', false); - uriValidator.reset(); + uriValidator.calls.reset(); writer.start('img', {src:'someImgUrl'}, false); expect(uriValidator).toHaveBeenCalledWith('someImgUrl', true); - uriValidator.reset(); + uriValidator.calls.reset(); writer.start('someTag', {src:'someNonUrl'}, false); expect(uriValidator).not.toHaveBeenCalled(); }); it('should drop non valid uri attributes', function() { - uriValidator.andReturn(false); + uriValidator.and.returnValue(false); writer.start('a', {href:'someUrl'}, false); expect(html).toEqual(''); html = ''; - uriValidator.andReturn(true); + uriValidator.and.returnValue(true); writer.start('a', {href:'someUrl'}, false); expect(html).toEqual(''); }); @@ -368,55 +529,51 @@ describe('HTML', function() { describe('uri checking', function() { beforeEach(function() { - this.addMatchers({ + jasmine.addMatchers({ toBeValidUrl: function() { - var sanitize; - inject(function($sanitize) { - sanitize = $sanitize; - }); - var input = ''; - return sanitize(input) === input; - }, - toBeValidImageSrc: function() { - var sanitize; - inject(function($sanitize) { - sanitize = $sanitize; - }); - var input = ''; - return sanitize(input) === input; + return { + compare: function(actual) { + var sanitize; + inject(function($sanitize) { + sanitize = $sanitize; + }); + var input = ''; + return { pass: sanitize(input) === input }; + } + }; } }); }); - it('should use $$sanitizeUri for links', function() { + it('should use $$sanitizeUri for a[href] links', function() { var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri'); module(function($provide) { $provide.value('$$sanitizeUri', $$sanitizeUri); }); inject(function() { - $$sanitizeUri.andReturn('someUri'); + $$sanitizeUri.and.returnValue('someUri'); expectHTML('').toEqual(''); expect($$sanitizeUri).toHaveBeenCalledWith('someUri', false); - $$sanitizeUri.andReturn('unsafe:someUri'); + $$sanitizeUri.and.returnValue('unsafe:someUri'); expectHTML('').toEqual(''); }); }); - it('should use $$sanitizeUri for links', function() { + it('should use $$sanitizeUri for img[src] links', function() { var $$sanitizeUri = jasmine.createSpy('$$sanitizeUri'); module(function($provide) { $provide.value('$$sanitizeUri', $$sanitizeUri); }); inject(function() { - $$sanitizeUri.andReturn('someUri'); + $$sanitizeUri.and.returnValue('someUri'); - expectHTML('').toEqual(''); + expectHTML('').toEqual(''); expect($$sanitizeUri).toHaveBeenCalledWith('someUri', true); - $$sanitizeUri.andReturn('unsafe:someUri'); - expectHTML('').toEqual(''); + $$sanitizeUri.and.returnValue('unsafe:someUri'); + expectHTML('').toEqual(''); }); }); @@ -437,13 +594,13 @@ describe('HTML', function() { }); it('should not be URI', function() { - /* jshint scripturl: true */ + // eslint-disable-next-line no-script-url expect('javascript:alert').not.toBeValidUrl(); }); describe('javascript URLs', function() { it('should ignore javascript:', function() { - /* jshint scripturl: true */ + // eslint-disable-next-line no-script-url expect('JavaScript:abc').not.toBeValidUrl(); expect(' \n Java\n Script:abc').not.toBeValidUrl(); expect('http://JavaScript/my.js').toBeValidUrl(); @@ -455,7 +612,7 @@ describe('HTML', function() { expect('j avascript:').not.toBeValidUrl(); }); - it('should ignore decimal with leading 0 encodede javascript:', function() { + it('should ignore decimal with leading 0 encoded javascript:', function() { expect('javascript:').not.toBeValidUrl(); expect('j avascript:').not.toBeValidUrl(); expect('j avascript:').not.toBeValidUrl(); @@ -487,8 +644,7 @@ describe('HTML', function() { }); describe('decodeEntities', function() { - var handler, text, - origHiddenPre = window.hiddenPre; + var handler, text; beforeEach(function() { text = ''; @@ -503,37 +659,13 @@ describe('decodeEntities', function() { module('ngSanitize'); }); - afterEach(function() { - window.hiddenPre = origHiddenPre; + it('should unescape text', function() { + htmlParser('a<div>&</div>c', handler); + expect(text).toEqual('a
          &
          c'); }); - it('should use innerText if textContent is not available (IE<9)', function() { - window.hiddenPre = { - innerText: 'INNER_TEXT' - }; - inject(function($sanitize) { - htmlParser('text', handler); - expect(text).toEqual('INNER_TEXT'); - }); - }); - it('should use textContent if available', function() { - window.hiddenPre = { - textContent: 'TEXT_CONTENT', - innerText: 'INNER_TEXT' - }; - inject(function($sanitize) { - htmlParser('text', handler); - expect(text).toEqual('TEXT_CONTENT'); - }); - }); - it('should use textContent even if empty', function() { - window.hiddenPre = { - textContent: '', - innerText: 'INNER_TEXT' - }; - inject(function($sanitize) { - htmlParser('text', handler); - expect(text).toEqual(''); - }); + it('should preserve whitespace', function() { + htmlParser(' a&b ', handler); + expect(text).toEqual(' a&b '); }); }); diff --git a/test/ngScenario/ApplicationSpec.js b/test/ngScenario/ApplicationSpec.js deleted file mode 100644 index b86a4e5eccdc..000000000000 --- a/test/ngScenario/ApplicationSpec.js +++ /dev/null @@ -1,147 +0,0 @@ -'use strict'; - -describe('angular.scenario.Application', function() { - var $window; - var app, frames; - - function callLoadHandlers(app) { - var handler = app.getFrame_().triggerHandler('load'); - } - - beforeEach(function() { - document.body.innerHTML = ''; - frames = _jQuery("
          "); - _jQuery(document.body).append(frames); - app = new angular.scenario.Application(frames); - }); - - - afterEach(function() { - _jQuery('iframe').off(); // cleanup any leftover onload handlers - document.body.innerHTML = ''; - }); - - - it('should return new $window and $document after navigateTo', function() { - var called; - var testWindow, testDocument, counter = 0; - app.getWindow_ = function() { - return {x:counter++, document:{x:counter++}}; - }; - app.navigateTo('http://www.google.com/'); - app.executeAction(function($window, $document) { - testWindow = $window; - testDocument = $document; - }); - app.navigateTo('http://www.google.com/'); - app.executeAction(function($window, $document) { - expect($window).not.toEqual(testWindow); - expect($document).not.toEqual(testDocument); - called = true; - }); - expect(called).toBeTruthy(); - }); - - it('should execute callback with correct arguments', function() { - var called; - var testWindow = {document: {}}; - app.getWindow_ = function() { - return testWindow; - }; - app.executeAction(function($window, $document) { - expect(this).toEqual(app); - expect($document).toEqual(_jQuery($window.document)); - expect($window).toEqual(testWindow); - called = true; - }); - expect(called).toBeTruthy(); - }); - - it('should use a new iframe each time', function() { - app.navigateTo('http://localhost/'); - var frame = app.getFrame_(); - frame.attr('test', true); - app.navigateTo('http://localhost/'); - expect(app.getFrame_().attr('test')).toBeFalsy(); - }); - - it('should call error handler if document not accessible', function() { - var called; - app.getWindow_ = function() { - return {}; - }; - app.navigateTo('http://localhost/', angular.noop, function(error) { - expect(error).toMatch(/Sandbox Error/); - called = true; - }); - callLoadHandlers(app); - expect(called).toBeTruthy(); - }); - - it('should call error handler if navigating to about:blank', function() { - var called; - app.navigateTo('about:blank', angular.noop, function(error) { - expect(error).toMatch(/Sandbox Error/); - called = true; - }); - expect(called).toBeTruthy(); - }); - - it('should remove old iframes', function() { - app.navigateTo('http://localhost/#foo'); - frames.find('iframe')[0].id = 'test'; - - app.navigateTo('http://localhost/#bar'); - var iframes = frames.find('iframe'); - - expect(iframes.length).toEqual(1); - expect(iframes[0].src).toEqual('http://localhost/#bar'); - expect(iframes[0].id).toBeFalsy(); - }); - - it('should URL update description bar', function() { - app.navigateTo('http://localhost/'); - var anchor = frames.find('> h2 a'); - expect(anchor.attr('href')).toEqual('http://localhost/'); - expect(anchor.text()).toEqual('http://localhost/'); - }); - - it('should call onload handler when frame loads', function() { - var called; - app.getWindow_ = function() { - return {document: {}}; - }; - app.navigateTo('http://localhost/', function($window, $document) { - called = true; - }); - callLoadHandlers(app); - expect(called).toBeTruthy(); - }); - - it('should wait for pending requests in executeAction', inject(function($injector, $browser) { - var called, polled; - var handlers = []; - var testWindow = { - document: jqLite('
          ')[0], - angular: { - element: jqLite, - service: {} - } - }; - $browser.notifyWhenNoOutstandingRequests = function(fn) { - handlers.push(fn); - }; - jqLite(testWindow.document).data('$injector', $injector); - app.getWindow_ = function() { - return testWindow; - }; - app.executeAction(function($window, $document) { - expect($window).toEqual(testWindow); - expect($document).toBeDefined(); - expect($document[0].className).toEqual('test-foo'); - }); - expect(handlers.length).toEqual(1); - handlers[0](); - dealoc(testWindow.document); - })); -}); diff --git a/test/ngScenario/DescribeSpec.js b/test/ngScenario/DescribeSpec.js deleted file mode 100644 index 74b385ee78ef..000000000000 --- a/test/ngScenario/DescribeSpec.js +++ /dev/null @@ -1,122 +0,0 @@ -'use strict'; - -describe('angular.scenario.Describe', function() { - var log; - var root; - - beforeEach(function() { - root = new angular.scenario.Describe(); - - /** - * Simple callback logging system. Use to assert proper order of calls. - */ - log = function(text) { - log.text = log.text + text; - }; - log.fn = function(text) { - return function(done) { - log(text); - (done || angular.noop)(); - }; - }; - log.reset = function() { - log.text = ''; - }; - log.reset(); - }); - - it('should handle basic nested case', function() { - root.describe('A', function() { - this.beforeEach(log.fn('{')); - this.afterEach(log.fn('}')); - this.it('1', log.fn('1')); - this.describe('B', function() { - this.beforeEach(log.fn('(')); - this.afterEach(log.fn(')')); - this.it('2', log.fn('2')); - }); - }); - var specs = root.getSpecs(); - expect(specs.length).toEqual(2); - - expect(specs[0].name).toEqual('2'); - specs[0].before(); - specs[0].body(); - specs[0].after(); - expect(log.text).toEqual('{(2)}'); - - log.reset(); - expect(specs[1].name).toEqual('1'); - specs[1].before(); - specs[1].body(); - specs[1].after(); - expect(log.text).toEqual('{1}'); - }); - - it('should link nested describe blocks with parent and children', function() { - root.describe('A', function() { - this.it('1', angular.noop); - this.describe('B', function() { - this.it('2', angular.noop); - this.describe('C', function() { - this.it('3', angular.noop); - }); - }); - }); - var specs = root.getSpecs(); - expect(specs[2].definition.parent).toEqual(root); - expect(specs[0].definition.parent).toEqual(specs[2].definition.children[0]); - }); - - it('should not process xit and xdescribe', function() { - root.describe('A', function() { - this.xit('1', angular.noop); - this.xdescribe('B', function() { - this.it('2', angular.noop); - this.describe('C', function() { - this.it('3', angular.noop); - }); - }); - }); - var specs = root.getSpecs(); - expect(specs.length).toEqual(0); - }); - - it('should only return iit and ddescribe if present', function() { - root.describe('A', function() { - this.it('1', angular.noop); - this.iit('2', angular.noop); - this.describe('B', function() { - this.it('3', angular.noop); - this.ddescribe('C', function() { - this.it('4', angular.noop); - this.describe('D', function() { - this.it('5', angular.noop); - }); - }); - }); - }); - var specs = root.getSpecs(); - expect(specs.length).toEqual(3); - expect(specs[0].name).toEqual('5'); - expect(specs[1].name).toEqual('4'); - expect(specs[2].name).toEqual('2'); - }); - - it('should create uniqueIds in the tree', function() { - angular.scenario.Describe.id = 0; - var a = new angular.scenario.Describe(); - var b = new angular.scenario.Describe(); - expect(a.id).toNotEqual(b.id); - }); - - it('should create uniqueIds for each spec', function() { - var d = new angular.scenario.Describe(); - d.it('fake', function() {}); - d.it('fake', function() {}); - - expect(d.its[0].id).toBeDefined(); - expect(d.its[1].id).toBeDefined(); - expect(d.its[0].id).not.toEqual(d.its[1].id); - }); -}); diff --git a/test/ngScenario/FutureSpec.js b/test/ngScenario/FutureSpec.js deleted file mode 100644 index 9ae284dc8e17..000000000000 --- a/test/ngScenario/FutureSpec.js +++ /dev/null @@ -1,77 +0,0 @@ -'use strict'; - -describe('angular.scenario.Future', function() { - var future; - - it('should set the sane defaults', function() { - var behavior = function() {}; - var future = new angular.scenario.Future('test name', behavior, 'foo'); - expect(future.name).toEqual('test name'); - expect(future.behavior).toEqual(behavior); - expect(future.line).toEqual('foo'); - expect(future.value).toBeUndefined(); - expect(future.fulfilled).toBeFalsy(); - expect(future.parser).toEqual(angular.identity); - }); - - it('should be fulfilled after execution and done callback', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(); - }); - future.execute(angular.noop); - expect(future.fulfilled).toBeTruthy(); - }); - - it('should take callback with (error, result) and forward', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(10, 20); - }); - future.execute(function(error, result) { - expect(error).toEqual(10); - expect(result).toEqual(20); - }); - }); - - it('should use error as value if provided', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(10, 20); - }); - future.execute(angular.noop); - expect(future.value).toEqual(10); - }); - - it('should parse json with fromJson', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(null, '{"test": "foo"}'); - }); - future.fromJson().execute(angular.noop); - expect(future.value).toEqual({test: 'foo'}); - }); - - it('should convert to json with toJson', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(null, {test: 'foo'}); - }); - future.toJson().execute(angular.noop); - expect(future.value).toEqual('{"test":"foo"}'); - }); - - it('should convert with custom parser', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(null, 'foo'); - }); - future.parsedWith(function(value) { - return value.toUpperCase(); - }).execute(angular.noop); - expect(future.value).toEqual('FOO'); - }); - - it('should pass error if parser fails', function() { - var future = new angular.scenario.Future('test name', function(done) { - done(null, '{'); - }); - future.fromJson().execute(function(error, result) { - expect(error).toBeDefined(); - }); - }); -}); diff --git a/test/ngScenario/ObjectModelSpec.js b/test/ngScenario/ObjectModelSpec.js deleted file mode 100644 index e0da628d7c61..000000000000 --- a/test/ngScenario/ObjectModelSpec.js +++ /dev/null @@ -1,333 +0,0 @@ -'use strict'; - -describe('angular.scenario.ObjectModel', function() { - var model; - var runner; - var spec, step; - - function buildSpec(id, name, definitions) { - var spec = { - id: id, - name: name, - definition: { - name: definitions.shift() - } - }; - var currentDef = spec.definition; - - forEach(definitions, function(defName) { - currentDef.parent = { - name: defName - }; - currentDef = currentDef.parent; - }); - - return spec; - } - - function buildStep(name, line) { - return { - name: name || 'test step', - line: function() { return line || ''; } - }; - } - - beforeEach(function() { - spec = buildSpec(1, 'test spec', ['describe 1']); - step = buildStep(); - runner = new angular.scenario.testing.MockRunner(); - model = new angular.scenario.ObjectModel(runner); - }); - - it('should value default empty value', function() { - expect(model.value).toEqual({ - name: '', - children: [] - }); - }); - - it('should add spec and create describe blocks on SpecBegin event', function() { - runner.emit('SpecBegin', buildSpec(1, 'test spec', ['describe 2', 'describe 1'])); - - expect(model.value.children['describe 1']).toBeDefined(); - expect(model.value.children['describe 1'].children['describe 2']).toBeDefined(); - expect(model.value.children['describe 1'].children['describe 2'].specs['test spec']).toBeDefined(); - }); - - it('should set fullDefinitionName on SpecBegin event', function() { - runner.emit('SpecBegin', buildSpec(1, 'fake spec', ['describe 2'])); - var spec = model.getSpec(1); - - expect(spec.fullDefinitionName).toBeDefined(); - expect(spec.fullDefinitionName).toEqual('describe 2'); - }); - - it('should set fullDefinitionName on SpecBegin event (join more names by space)', function() { - runner.emit('SpecBegin', buildSpec(1, 'fake spec', ['describe 2', 'describe 1'])); - var spec = model.getSpec(1); - - expect(spec.fullDefinitionName).toEqual('describe 1 describe 2'); - }); - - it('should add step to spec on StepBegin', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - expect(model.value.children['describe 1'].specs['test spec'].steps.length).toEqual(1); - }); - - it('should update spec timer duration on SpecEnd event', function() { - runner.emit('SpecBegin', spec); - runner.emit('SpecEnd', spec); - - expect(model.value.children['describe 1'].specs['test spec'].duration).toBeDefined(); - }); - - it('should update step timer duration on StepEnd event', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - expect(model.value.children['describe 1'].specs['test spec'].steps[0].duration).toBeDefined(); - }); - - it('should set spec status on SpecEnd to success if no status set', function() { - runner.emit('SpecBegin', spec); - runner.emit('SpecEnd', spec); - - expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('success'); - }); - - it('should set status to error after SpecError', function() { - runner.emit('SpecBegin', spec); - runner.emit('SpecError', spec, 'error'); - - expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('error'); - }); - - it('should set spec status to failure if step fails', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('failure'); - }); - - it('should set spec status to error if step errors', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepError', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - expect(model.value.children['describe 1'].specs['test spec'].status).toEqual('error'); - }); - - describe('events', function() { - var Spec = angular.scenario.ObjectModel.Spec, - Step = angular.scenario.ObjectModel.Step, - callback; - - beforeEach(function() { - callback = jasmine.createSpy('listener'); - }); - - it('should provide method for registering a listener', function() { - expect(model.on).toBeDefined(); - expect(model.on instanceof Function).toBe(true); - }); - - it('should forward SpecBegin event', function() { - model.on('SpecBegin', callback); - runner.emit('SpecBegin', spec); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward SpecBegin event with ObjectModel.Spec as a param', function() { - model.on('SpecBegin', callback); - runner.emit('SpecBegin', spec); - - expect(callback.mostRecentCall.args[0] instanceof Spec).toBe(true); - expect(callback.mostRecentCall.args[0].name).toEqual(spec.name); - }); - - it('should forward SpecError event', function() { - model.on('SpecError', callback); - runner.emit('SpecBegin', spec); - runner.emit('SpecError', spec, {}); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward SpecError event with ObjectModel.Spec and error as a params', function() { - var error = {}; - model.on('SpecError', callback); - runner.emit('SpecBegin', spec); - runner.emit('SpecError', spec, error); - - var param = callback.mostRecentCall.args[0]; - expect(param instanceof Spec).toBe(true); - expect(param.name).toEqual(spec.name); - expect(param.status).toEqual('error'); - expect(param.error).toBe(error); - }); - - it('should forward SpecEnd event', function() { - model.on('SpecEnd', callback); - runner.emit('SpecBegin', spec); - runner.emit('SpecEnd', spec); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward SpecEnd event with ObjectModel.Spec as a param', function() { - model.on('SpecEnd', callback); - runner.emit('SpecBegin', spec); - runner.emit('SpecEnd', spec); - - expect(callback.mostRecentCall.args[0] instanceof Spec).toBe(true); - expect(callback.mostRecentCall.args[0].name).toEqual(spec.name); - }); - - it('should forward StepBegin event', function() { - model.on('StepBegin', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward StepBegin event with Spec and Step as params', function() { - model.on('StepBegin', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - - var params = callback.mostRecentCall.args; - expect(params[0] instanceof Spec).toBe(true); - expect(params[0].name).toEqual(spec.name); - expect(params[1] instanceof Step).toBe(true); - }); - - it('should forward StepError event', function() { - model.on('StepError', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepError', spec, step, {}); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward StepError event with Spec, Step and error as params', function() { - var error = {}; - model.on('StepError', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepError', spec, step, error); - - var params = callback.mostRecentCall.args; - expect(params[0] instanceof Spec).toBe(true); - expect(params[0].name).toEqual(spec.name); - expect(params[1] instanceof Step).toBe(true); - expect(params[1].status).toEqual('error'); - expect(params[2]).toBe(error); - }); - - it('should forward StepFailure event', function() { - model.on('StepFailure', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, {}); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward StepFailure event with Spec, Step and error as params', function() { - var error = {}; - model.on('StepFailure', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, error); - - var params = callback.mostRecentCall.args; - expect(params[0] instanceof Spec).toBe(true); - expect(params[0].name).toEqual(spec.name); - expect(params[1] instanceof Step).toBe(true); - expect(params[1].status).toEqual('failure'); - expect(params[2]).toBe(error); - }); - - it('should forward StepEnd event', function() { - model.on('StepEnd', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - - expect(callback).toHaveBeenCalled(); - }); - - it('should forward StepEnd event with Spec and Step as params', function() { - model.on('StepEnd', callback); - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - - var params = callback.mostRecentCall.args; - expect(params[0] instanceof Spec).toBe(true); - expect(params[0].name).toEqual(spec.name); - expect(params[1] instanceof Step).toBe(true); - }); - - it('should forward RunnerEnd event', function() { - model.on('RunnerEnd', callback); - runner.emit('RunnerEnd'); - expect(callback).toHaveBeenCalled(); - }); - - it('should set error of first failure', function() { - var error = 'first-error', - step2 = buildStep(); - - model.on('SpecEnd', function(spec) { - expect(spec.error).toBeDefined(); - expect(spec.error).toBe(error); - }); - - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, error); - runner.emit('StepBegin', spec, step2); - runner.emit('StepFailure', spec, step2, 'second-error'); - runner.emit('SpecEnd', spec); - }); - - it('should set line number of first failure', function() { - var step = buildStep('fake', 'first-line'), - step2 = buildStep('fake2', 'second-line'); - - model.on('SpecEnd', function(spec) { - expect(spec.line).toBeDefined(); - expect(spec.line).toBe('first-line'); - }); - - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, null); - runner.emit('StepBegin', spec, step2); - runner.emit('StepFailure', spec, step2, null); - runner.emit('SpecEnd', spec); - }); - }); -}); diff --git a/test/ngScenario/RunnerSpec.js b/test/ngScenario/RunnerSpec.js deleted file mode 100644 index 9f62504409b1..000000000000 --- a/test/ngScenario/RunnerSpec.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; - -/** - * Mock spec runner. - */ -function MockSpecRunner() {} -MockSpecRunner.prototype.run = function(spec, specDone) { - spec.before.call(this); - spec.body.call(this); - spec.after.call(this); - specDone(); -}; - -MockSpecRunner.prototype.addFuture = function(name, fn, line) { - return {name: name, fn: fn, line: line}; -}; - -describe('angular.scenario.Runner', function() { - var $window; - var runner; - - beforeEach(function() { - // Trick to get the scope out of a DSL statement - angular.scenario.dsl('dslAddFuture', function() { - return function() { - return this.addFuture('future name', angular.noop); - }; - }); - // Trick to get the scope out of a DSL statement - angular.scenario.dsl('dslScope', function() { - var scope = this; - return function() { return scope; }; - }); - // Trick to get the scope out of a DSL statement - angular.scenario.dsl('dslChain', function() { - return function() { - this.chained = 0; - this.chain = function() { this.chained++; return this; }; - return this; - }; - }); - $window = { - location: {} - }; - runner = new angular.scenario.Runner($window); - runner.on('SpecError', angular.mock.rethrow); - runner.on('StepError', angular.mock.rethrow); - }); - - afterEach(function() { - delete angular.scenario.dsl.dslScope; - delete angular.scenario.dsl.dslChain; - }); - - it('should publish the functions in the public API', function() { - angular.forEach(runner.api, function(fn, name) { - var func; - if (name in $window) { - func = $window[name]; - } - expect(angular.isFunction(func)).toBeTruthy(); - }); - }); - - it('should construct valid describe trees with public API', function() { - var before = []; - var after = []; - $window.describe('A', function() { - $window.beforeEach(function() { before.push('A'); }); - $window.afterEach(function() { after.push('A'); }); - $window.it('1', angular.noop); - $window.describe('B', function() { - $window.beforeEach(function() { before.push('B'); }); - $window.afterEach(function() { after.push('B'); }); - $window.it('2', angular.noop); - $window.describe('C', function() { - $window.beforeEach(function() { before.push('C'); }); - $window.afterEach(function() { after.push('C'); }); - $window.it('3', angular.noop); - }); - }); - }); - var specs = runner.rootDescribe.getSpecs(); - specs[0].before(); - specs[0].body(); - specs[0].after(); - expect(before).toEqual(['A', 'B', 'C']); - expect(after).toEqual(['C', 'B', 'A']); - expect(specs[2].definition.parent).toEqual(runner.rootDescribe); - expect(specs[0].definition.parent).toEqual(specs[2].definition.children[0]); - }); - - it('should publish the DSL statements to the $window', function() { - $window.describe('describe', function() { - $window.it('1', function() { - expect($window.dslScope).toBeDefined(); - }); - }); - runner.run(null/*application*/); - }); - - it('should create a new scope for each DSL chain', function() { - $window.describe('describe', function() { - $window.it('1', function() { - var scope = $window.dslScope(); - scope.test = "foo"; - expect($window.dslScope().test).toBeUndefined(); - }); - $window.it('2', function() { - var scope = $window.dslChain().chain().chain(); - expect(scope.chained).toEqual(2); - }); - }); - runner.run(null/*application*/); - }); -}); diff --git a/test/ngScenario/ScenarioSpec.js b/test/ngScenario/ScenarioSpec.js deleted file mode 100644 index a8e5280b5784..000000000000 --- a/test/ngScenario/ScenarioSpec.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -describe("ScenarioSpec: Compilation", function() { - var element; - - afterEach(function() { - dealoc(element); - }); - - - describe('compilation', function() { - it("should compile dom node and return scope", inject(function($rootScope, $compile) { - var node = jqLite('
          {{b=a+1}}
          ')[0]; - element = $compile(node)($rootScope); - $rootScope.$digest(); - expect($rootScope.a).toEqual(1); - expect($rootScope.b).toEqual(2); - })); - - it("should compile jQuery node and return scope", inject(function($rootScope, $compile) { - element = $compile(jqLite('
          {{a=123}}
          '))($rootScope); - $rootScope.$digest(); - expect(jqLite(element).text()).toEqual('123'); - })); - - it("should compile text node and return scope", inject(function($rootScope, $compile) { - element = $compile('
          {{a=123}}
          ')($rootScope); - $rootScope.$digest(); - expect(jqLite(element).text()).toEqual('123'); - })); - }); - - describe('jQuery', function() { - it('should exist on the angular.scenario object', function() { - expect(angular.scenario.jQuery).toBeDefined(); - }); - - it('should have common jQuery methods', function() { - var jQuery = angular.scenario.jQuery; - expect(typeof jQuery).toEqual('function'); - expect(typeof jQuery('
          ').html).toEqual('function'); - }); - }); -}); diff --git a/test/ngScenario/SpecRunnerSpec.js b/test/ngScenario/SpecRunnerSpec.js deleted file mode 100644 index 5970607ce18e..000000000000 --- a/test/ngScenario/SpecRunnerSpec.js +++ /dev/null @@ -1,176 +0,0 @@ -'use strict'; - -/** - * Mock Application - */ -function ApplicationMock($window) { - this.$window = $window; -} -ApplicationMock.prototype = { - executeAction: function(callback) { - callback.call(this.$window, _jQuery(this.$window.document), this.$window); - } -}; - -describe('angular.scenario.SpecRunner', function() { - var $window, $root, log; - var runner; - - function createSpec(name, body) { - return { - name: name, - before: angular.noop, - body: body || angular.noop, - after: angular.noop - }; - } - - beforeEach(inject(function($rootScope) { - log = []; - $window = {}; - $window.setTimeout = function(fn, timeout) { - fn(); - }; - $root = $rootScope; - $root.emit = function(eventName) { - log.push(eventName); - }; - $root.on = function(eventName) { - log.push('Listener Added for ' + eventName); - }; - $root.application = new ApplicationMock($window); - $root.$window = $window; - runner = $root.$new(); - - var Cls = angular.scenario.SpecRunner; - for (var name in Cls.prototype) - runner[name] = angular.bind(runner, Cls.prototype[name]); - - Cls.call(runner); - })); - - it('should bind futures to the spec', function() { - runner.addFuture('test future', function(done) { - this.value = 10; - done(); - }); - runner.futures[0].execute(angular.noop); - expect(runner.value).toEqual(10); - }); - - it('should pass done to future action behavior', function() { - runner.addFutureAction('test future', function($window, $document, done) { - expect(angular.isFunction(done)).toBeTruthy(); - done(10, 20); - }); - runner.futures[0].execute(function(error, result) { - expect(error).toEqual(10); - expect(result).toEqual(20); - }); - }); - - it('should execute spec function and notify UI', function() { - var finished; - var spec = createSpec('test spec', function() { - this.test = 'some value'; - }); - runner.addFuture('test future', function(done) { - done(); - }); - runner.run(spec, function() { - finished = true; - }); - expect(runner.test).toEqual('some value'); - expect(finished).toBeTruthy(); - expect(log).toEqual([ - 'SpecBegin', - 'StepBegin', - 'StepEnd', - 'SpecEnd' - ]); - }); - - it('should execute notify UI on spec setup error', function() { - var finished; - var spec = createSpec('test spec', function() { - throw 'message'; - }); - runner.run(spec, function() { - finished = true; - }); - expect(finished).toBeTruthy(); - expect(log).toEqual([ - 'SpecBegin', - 'SpecError', - 'SpecEnd' - ]); - }); - - it('should execute notify UI on step failure', function() { - var finished; - var spec = createSpec('test spec'); - runner.addFuture('test future', function(done) { - done('failure message'); - }); - runner.run(spec, function() { - finished = true; - }); - expect(finished).toBeTruthy(); - expect(log).toEqual([ - 'SpecBegin', - 'StepBegin', - 'StepFailure', - 'StepEnd', - 'SpecEnd' - ]); - }); - - it('should execute notify UI on step error', function() { - var finished; - var spec = createSpec('test spec', function() { - this.addFuture('test future', function(done) { - throw 'error message'; - }); - }); - runner.run(spec, function() { - finished = true; - }); - expect(finished).toBeTruthy(); - expect(log).toEqual([ - 'SpecBegin', - 'StepBegin', - 'StepError', - 'StepEnd', - 'SpecEnd' - ]); - }); - - it('should run after handlers even if error in body of spec', function() { - var finished, after; - var spec = createSpec('test spec', function() { - this.addFuture('body', function(done) { - throw 'error message'; - }); - }); - spec.after = function() { - this.addFuture('after', function(done) { - after = true; - done(); - }); - }; - runner.run(spec, function() { - finished = true; - }); - expect(finished).toBeTruthy(); - expect(after).toBeTruthy(); - expect(log).toEqual([ - 'SpecBegin', - 'StepBegin', - 'StepError', - 'StepEnd', - 'StepBegin', - 'StepEnd', - 'SpecEnd' - ]); - }); -}); diff --git a/test/ngScenario/dslSpec.js b/test/ngScenario/dslSpec.js deleted file mode 100644 index 2287b6bf3071..000000000000 --- a/test/ngScenario/dslSpec.js +++ /dev/null @@ -1,785 +0,0 @@ -'use strict'; - -describe("angular.scenario.dsl", function() { - var element; - var $window, $root; - var eventLog; - - afterEach(function() { - dealoc(element); - }); - - beforeEach(module('ngSanitize')); - - beforeEach(inject(function($injector) { - eventLog = []; - $window = { - document: window.document.body, - angular: new angular.scenario.testing.MockAngular() - }; - jqLite($window.document).data('$injector', $injector).attr('ng-app', '').addClass('html'); - $root = $injector.get('$rootScope'); - $root.emit = function(eventName) { - eventLog.push(eventName); - }; - $root.on = function(eventName) { - eventLog.push('Listener Added for ' + eventName); - }; - $root.futures = []; - $root.futureLog = []; - $root.$window = $window; - $root.addFuture = function(name, fn) { - this.futures.push(name); - fn.call(this, function(error, result) { - $root.futureError = error; - $root.futureResult = result; - $root.futureLog.push(name); - }); - }; - $root.dsl = {}; - angular.forEach(angular.scenario.dsl, function(fn, name) { - $root.dsl[name] = function() { - return fn.call($root).apply($root, arguments); - }; - }); - $root.application = new angular.scenario.Application(jqLite($window.document)); - $root.application.getWindow_ = function() { - return $window; - }; - $root.application.navigateTo = function(url, callback) { - $window.location = url; - callback(); - }; - // Just use the real one since it delegates to this.addFuture - $root.addFutureAction = angular.scenario. - SpecRunner.prototype.addFutureAction; - jqLite($window.document).empty(); - })); - - afterEach(function() { - jqLite($window.document).removeData('$injector'); - }); - - describe('Pause', function() { - it('should pause until resume to complete', function() { - expect($window.resume).toBeUndefined(); - $root.dsl.pause(); - expect(angular.isFunction($window.resume)).toBeTruthy(); - expect($root.futureLog).toEqual([]); - $window.resume(); - expect($root.futureLog). - toEqual(['pausing for you to resume']); - expect(eventLog).toContain('InteractivePause'); - }); - }); - - describe('Sleep', function() { - beforeEach(function() { - $root.$window.setTimeout = function(fn, value) { - $root.timerValue = value; - fn(); - }; - }); - - it('should sleep for specified seconds', function() { - $root.dsl.sleep(10); - expect($root.timerValue).toEqual(10000); - expect($root.futureResult).toEqual(10000); - }); - }); - - describe('Expect', function() { - it('should chain and execute matcher', function() { - var future = {value: 10}; - var result = $root.dsl.expect(future); - result.toEqual(10); - expect($root.futureError).toBeUndefined(); - expect($root.futureResult).toBeUndefined(); - result = $root.dsl.expect(future); - result.toEqual(20); - expect($root.futureError).toBeDefined(); - }); - }); - - describe('Browser', function() { - describe('Reload', function() { - it('should navigateTo', function() { - $window.location = { - href: '#foo' - }; - $root.dsl.browser().reload(); - expect($root.futureResult).toEqual('#foo'); - expect($window.location).toEqual('#foo'); - }); - }); - - describe('NavigateTo', function() { - it('should allow a string url', function() { - $root.dsl.browser().navigateTo('http://myurl'); - expect($window.location).toEqual('http://myurl'); - expect($root.futureResult).toEqual('http://myurl'); - }); - - it('should allow a future url', function() { - $root.dsl.browser().navigateTo('http://myurl', function() { - return 'http://futureUrl/'; - }); - expect($window.location).toEqual('http://futureUrl/'); - expect($root.futureResult).toEqual('http://futureUrl/'); - }); - - it('should complete if angular is missing from app frame', function() { - delete $window.angular; - $root.dsl.browser().navigateTo('http://myurl'); - expect($window.location).toEqual('http://myurl'); - expect($root.futureResult).toEqual('http://myurl'); - }); - }); - - describe('window', function() { - beforeEach(function() { - $window.location = { - href: 'http://myurl/some/path?foo=10#/bar?x=2', - pathname: '/some/path', - search: '?foo=10', - hash: '#bar?x=2' - }; - }); - - it('should return full URL for href', function() { - $root.dsl.browser().window().href(); - expect($root.futureResult).toEqual($window.location.href); - }); - - it('should return the pathname', function() { - $root.dsl.browser().window().path(); - expect($root.futureResult).toEqual($window.location.pathname); - }); - - it('should return the search part', function() { - $root.dsl.browser().window().search(); - expect($root.futureResult).toEqual($window.location.search); - }); - - it('should return the hash without the #', function() { - $root.dsl.browser().window().hash(); - expect($root.futureResult).toEqual('bar?x=2'); - }); - }); - - describe('location', function() { - beforeEach(inject(function($injector) { - angular.extend($injector.get('$location'), { - url: function() {return '/path?search=a#hhh';}, - path: function() {return '/path';}, - search: function() {return {search: 'a'};}, - hash: function() {return 'hhh';} - }); - })); - - it('should return full url', function() { - $root.dsl.browser().location().url(); - expect($root.futureResult).toEqual('/path?search=a#hhh'); - }); - - it('should return the pathname', function() { - $root.dsl.browser().location().path(); - expect($root.futureResult).toEqual('/path'); - }); - - it('should return the query string as an object', function() { - $root.dsl.browser().location().search(); - expect($root.futureResult).toEqual({search: 'a'}); - }); - - it('should return the hash without the #', function() { - $root.dsl.browser().location().hash(); - expect($root.futureResult).toEqual('hhh'); - }); - }); - }); - - describe('Element Finding', function() { - var doc; - beforeEach(inject(function($injector) { - doc = _jQuery($window.document).append('
          ').find('.body'); - })); - - describe('Select', function() { - it('should select single option', function() { - doc.append( - '' - ); - $root.dsl.select('test').option('A'); - expect(doc.find('[ng-model="test"]').val()).toEqual('A'); - }); - - it('should select single option using data-ng', function() { - doc.append( - '' - ); - $root.dsl.select('test').option('A'); - expect(doc.find('[data-ng-model="test"]').val()).toEqual('A'); - }); - - it('should select single option using x-ng', function() { - doc.append( - '' - ); - $root.dsl.select('test').option('A'); - expect(doc.find('[x-ng-model="test"]').val()).toEqual('A'); - }); - - it('should select option by exact name', function() { - doc.append( - '' - ); - $root.dsl.select('test').option('one'); - expect(doc.find('[ng-model="test"]').val()).toEqual('D'); - }); - - it('should select option by name if no exact match and name contains value', function() { - doc.append( - '' - ); - $root.dsl.select('test').option('one'); - expect(doc.find('[ng-model="test"]').val()).toEqual('A'); - }); - - it('should select multiple options', function() { - doc.append( - '' - ); - $root.dsl.select('test').options('A', 'B'); - expect(doc.find('[ng-model="test"]').val()).toEqual(['A','B']); - }); - - it('should fail to select multiple options on non-multiple select', function() { - doc.append(''); - $root.dsl.select('test').options('A', 'B'); - expect($root.futureError).toMatch(/did not match/); - }); - - it('should fail to select an option that does not exist', function() { - doc.append( - '' - ); - $root.dsl.select('test').option('three'); - expect($root.futureError).toMatch(/not found/); - }); - }); - - describe('Element', function() { - it('should execute click', function() { - var clicked; - // Hash is important, otherwise we actually - // go to a different page and break the runner - doc.append(''); - doc.find('a').click(function() { - clicked = true; - }); - $root.dsl.element('a').click(); - }); - - it('should navigate page if click on anchor', function() { - expect($window.location).not.toEqual('#foo'); - doc.append(''); - $root.dsl.element('a').click(); - expect($window.location).toMatch(/#foo$/); - }); - - it('should not navigate if click event was cancelled', function() { - var initLocation = $window.location, - elm = jqLite(''); - - doc.append(elm); - elm.on('click', function(event) { - event.preventDefault(); - }); - - $root.dsl.element('a').click(); - expect($window.location).toBe(initLocation); - dealoc(elm); - }); - - it('should execute dblclick', function() { - var clicked; - // Hash is important, otherwise we actually - // go to a different page and break the runner - doc.append(''); - doc.find('a').dblclick(function() { - clicked = true; - }); - $root.dsl.element('a').dblclick(); - }); - - it('should navigate page if dblclick on anchor', function() { - expect($window.location).not.toEqual('#foo'); - doc.append(''); - $root.dsl.element('a').dblclick(); - expect($window.location).toMatch(/#foo$/); - }); - - it('should not navigate if dblclick event was cancelled', function() { - var initLocation = $window.location, - elm = jqLite(''); - - doc.append(elm); - elm.on('dblclick', function(event) { - event.preventDefault(); - }); - - $root.dsl.element('a').dblclick(); - expect($window.location).toBe(initLocation); - dealoc(elm); - }); - - it('should execute mouseover', function() { - var mousedOver; - doc.append('
          '); - doc.find('div').mouseover(function() { - mousedOver = true; - }); - $root.dsl.element('div').mouseover(); - expect(mousedOver).toBe(true); - }); - - it('should bubble up the mouseover event', function() { - var mousedOver; - doc.append('
          '); - doc.find('#outer').mouseover(function() { - mousedOver = true; - }); - $root.dsl.element('#inner').mouseover(); - expect(mousedOver).toBe(true); - }); - - it('should execute mousedown', function() { - var mousedDown; - doc.append('
          '); - doc.find('div').mousedown(function() { - mousedDown = true; - }); - $root.dsl.element('div').mousedown(); - expect(mousedDown).toBe(true); - }); - - it('should bubble up the mousedown event', function() { - var mousedDown; - doc.append('
          '); - doc.find('#outer').mousedown(function() { - mousedDown = true; - }); - $root.dsl.element('#inner').mousedown(); - expect(mousedDown).toBe(true); - }); - - it('should execute mouseup', function() { - var mousedUp; - doc.append('
          '); - doc.find('div').mouseup(function() { - mousedUp = true; - }); - $root.dsl.element('div').mouseup(); - expect(mousedUp).toBe(true); - }); - - it('should bubble up the mouseup event', function() { - var mousedUp; - doc.append('
          '); - doc.find('#outer').mouseup(function() { - mousedUp = true; - }); - $root.dsl.element('#inner').mouseup(); - expect(mousedUp).toBe(true); - }); - - it('should count matching elements', function() { - doc.append(''); - $root.dsl.element('span').count(); - expect($root.futureResult).toEqual(2); - }); - - it('should return count of 0 if no matching elements', function() { - $root.dsl.element('span').count(); - expect($root.futureResult).toEqual(0); - }); - - it('should get attribute', function() { - doc.append('
          '); - $root.dsl.element('#test').attr('class'); - expect($root.futureResult).toEqual('foo'); - }); - - it('should set attribute', function() { - doc.append('
          '); - $root.dsl.element('#test').attr('class', 'bam'); - expect(doc.find('#test').attr('class')).toEqual('bam'); - }); - - it('should get property', function() { - doc.append('
          '); - $root.dsl.element('#test').prop('className'); - expect($root.futureResult).toEqual('foo'); - }); - - it('should set property', function() { - doc.append('
          '); - $root.dsl.element('#test').prop('className', 'bam'); - expect(doc.find('#test').prop('className')).toEqual('bam'); - }); - - it('should get css', function() { - doc.append('
          '); - $root.dsl.element('#test').css('height'); - expect($root.futureResult).toMatch(/30px/); - }); - - it('should set css', function() { - doc.append('
          '); - $root.dsl.element('#test').css('height', '20px'); - expect(doc.find('#test').css('height')).toEqual('20px'); - }); - - it('should add all jQuery key/value methods', function() { - var METHODS = ['css', 'attr']; - var chain = $root.dsl.element('input'); - angular.forEach(METHODS, function(name) { - expect(angular.isFunction(chain[name])).toBeTruthy(); - }); - }); - - it('should get val', function() { - doc.append(''); - $root.dsl.element('input').val(); - expect($root.futureResult).toEqual('bar'); - }); - - it('should set val', function() { - doc.append(''); - $root.dsl.element('input').val('baz'); - expect(doc.find('input').val()).toEqual('baz'); - }); - - it('should use correct future name for generated set methods', function() { - doc.append(''); - $root.dsl.element('input').val(false); - expect($root.futures.pop()).toMatch(/element 'input' set val/); - }); - - it('should use correct future name for generated get methods', function() { - doc.append(''); - $root.dsl.element('input').val(); - expect($root.futures.pop()).toMatch(/element 'input' val/); - }); - - it('should add all jQuery property methods', function() { - var METHODS = [ - 'val', 'text', 'html', 'height', 'innerHeight', 'outerHeight', 'width', - 'innerWidth', 'outerWidth', 'position', 'scrollLeft', 'scrollTop', 'offset' - ]; - var chain = $root.dsl.element('input'); - angular.forEach(METHODS, function(name) { - expect(angular.isFunction(chain[name])).toBeTruthy(); - }); - }); - - it('should execute custom query', function() { - doc.append(''); - $root.dsl.element('#test').query(function(elements, done) { - done(null, elements.attr('href')); - }); - expect($root.futureResult).toEqual('http://example.com/myUrl'); - }); - - it('should use the selector as label if none is given', function() { - $root.dsl.element('mySelector'); - expect($root.label).toEqual('mySelector'); - }); - - it('should include the selector in paren when a label is given', function() { - $root.dsl.element('mySelector', 'myLabel'); - expect($root.label).toEqual('myLabel ( mySelector )'); - }); - }); - - describe('Repeater', function() { - var chain; - beforeEach(inject(function($compile, $rootScope) { - element = $compile( - '
          • {{i.name}} {{i.gender}}
          ')($rootScope); - $rootScope.items = [{name:'misko', gender:'male'}, {name:'felisa', gender:'female'}]; - $rootScope.$apply(); - doc.append(element); - chain = $root.dsl.repeater('ul li'); - })); - - it('should get the row count', function() { - chain.count(); - expect($root.futureResult).toEqual(2); - }); - - it('should return 0 if repeater doesnt match', inject(function($rootScope) { - $rootScope.items = []; - $rootScope.$apply(); - chain.count(); - expect($root.futureResult).toEqual(0); - })); - - it('should get a row of bindings', function() { - chain.row(1); - expect($root.futureResult).toEqual(['felisa', 'female']); - }); - - it('should get a column of bindings', function() { - chain.column('i.gender'); - expect($root.futureResult).toEqual(['male', 'female']); - }); - - it('should use the selector as label if none is given', function() { - expect($root.label).toEqual('ul li'); - }); - - it('should include the selector in paren when a label is given', function() { - $root.dsl.repeater('mySelector', 'myLabel'); - expect($root.label).toEqual('myLabel ( ul li mySelector )'); - }); - }); - - describe('Binding', function() { - var compile; - - beforeEach(inject(function($compile, $rootScope) { - compile = function(html, value) { - element = $compile(html)($rootScope); - doc.append(element); - $rootScope.foo = {bar: value || 'some value'}; - $rootScope.$apply(); - }; - })); - - - it('should select binding in interpolation', function() { - compile('{{ foo.bar }}'); - $root.dsl.binding('foo.bar'); - expect($root.futureResult).toEqual('some value'); - }); - - it('should select binding in multiple interpolations', function() { - compile('{{ foo.bar }}
          {{ true }}
          '); - $root.dsl.binding('foo.bar'); - expect($root.futureResult).toEqual('some value'); - - $root.dsl.binding('true'); - expect($root.futureResult).toEqual('true'); - }); - - it('should select binding by name', function() { - compile(''); - $root.dsl.binding('foo.bar'); - expect($root.futureResult).toEqual('some value'); - }); - - it('should select binding by regexp', function() { - compile('some value'); - $root.dsl.binding(/^foo\..+/); - expect($root.futureResult).toEqual('some value'); - }); - - it('should return innerHTML for all the other elements', function() { - compile('
          ', 'some value'); - $root.dsl.binding('foo.bar'); - expect($root.futureResult.toLowerCase()).toEqual('some value'); - }); - - it('should select binding in template by name', function() { - compile('
          ', 'bar');
          -        $root.dsl.binding('foo.bar');
          -        expect($root.futureResult).toEqual('bar');
          -      });
          -
          -      it('should match bindings by substring match', function() {
          -        compile('
          ', 'binding value');
          -        $root.dsl.binding('foo . bar');
          -        expect($root.futureResult).toEqual('binding value');
          -      });
          -
          -      it('should return error if no bindings in document', function() {
          -        $root.dsl.binding('foo.bar');
          -        expect($root.futureError).toMatch(/did not match/);
          -      });
          -
          -      it('should return error if no binding matches', function() {
          -        compile('some value');
          -        $root.dsl.binding('foo.bar');
          -        expect($root.futureError).toMatch(/did not match/);
          -      });
          -    });
          -
          -    describe('Using', function() {
          -      it('should prefix selector in $document.elements()', function() {
          -        var chain;
          -        doc.append(
          -          '
          ' + - '
          ' - ); - chain = $root.dsl.using('div#test2'); - chain.input('test.input').enter('foo'); - var inputs = _jQuery('input[ng-model="test.input"]'); - expect(inputs.first().val()).toEqual('something'); - expect(inputs.last().val()).toEqual('foo'); - }); - - it('should use the selector as label if none is given', function() { - $root.dsl.using('mySelector'); - expect($root.label).toEqual('mySelector'); - }); - - it('should include the selector in paren when a label is given', function() { - $root.dsl.using('mySelector', 'myLabel'); - expect($root.label).toEqual('myLabel ( mySelector )'); - }); - - }); - - describe('Input', function() { - it('should change value in text input', inject(function($compile) { - runs(function() { - element = $compile('')($root); - doc.append(element); - var chain = $root.dsl.input('test.input'); - chain.enter('foo'); - expect(_jQuery('input[ng-model="test.input"]').val()).toEqual('foo'); - }); - - // cleanup the event queue - waits(0); - - runs(function() { - expect($root.test.input).toBe('foo'); - }); - })); - - it('should change value in text input in dash form', function() { - doc.append(''); - var chain = $root.dsl.input('test.input'); - chain.enter('foo'); - expect(_jQuery('input[ng-model="test.input"]').val()).toEqual('foo'); - }); - it('should change value in text input in data-ng form', function() { - doc.append(''); - var chain = $root.dsl.input('test.input'); - chain.enter('foo'); - expect(_jQuery('input[data-ng-model="test.input"]').val()).toEqual('foo'); - }); - it('should change value in text input in x-ng form', function() { - doc.append(''); - var chain = $root.dsl.input('test.input'); - chain.enter('foo'); - expect(_jQuery('input[x-ng-model="test.input"]').val()).toEqual('foo'); - }); - - - - it('should return error if no input exists', function() { - var chain = $root.dsl.input('test.input'); - chain.enter('foo'); - expect($root.futureError).toMatch(/did not match/); - }); - - it('should toggle checkbox state', function() { - doc.append(''); - expect(_jQuery('input[ng-model="test.input"]'). - prop('checked')).toBe(true); - var chain = $root.dsl.input('test.input'); - chain.check(); - expect(_jQuery('input[ng-model="test.input"]'). - prop('checked')).toBe(false); - $window.angular.reset(); - chain.check(); - expect(_jQuery('input[ng-model="test.input"]'). - prop('checked')).toBe(true); - }); - - it('should return error if checkbox did not match', function() { - var chain = $root.dsl.input('test.input'); - chain.check(); - expect($root.futureError).toMatch(/did not match/); - }); - - it('should select option from radio group', function() { - doc.append( - '' + - '' - ); - // HACK! We don't know why this is sometimes false on chrome - _jQuery('input[ng\\:model="test.input"][value="bar"]').prop('checked', true); - expect(_jQuery('input[ng\\:model="test.input"][value="bar"]'). - prop('checked')).toBe(true); - expect(_jQuery('input[ng\\:model="test.input"][value="foo"]'). - prop('checked')).toBe(false); - var chain = $root.dsl.input('test.input'); - chain.select('foo'); - expect(_jQuery('input[ng\\:model="test.input"][value="bar"]'). - prop('checked')).toBe(false); - expect(_jQuery('input[ng\\:model="test.input"][value="foo"]'). - prop('checked')).toBe(true); - }); - - it('should return error if radio button did not match', function() { - var chain = $root.dsl.input('test.input'); - chain.select('foo'); - expect($root.futureError).toMatch(/did not match/); - }); - - describe('val', function() { - it('should return value in text input', function() { - doc.append(''); - $root.dsl.input('test.input').val(); - expect($root.futureResult).toEqual("something"); - }); - }); - }); - - describe('Textarea', function() { - - it('should change value in textarea', function() { - doc.append(''); - var chain = $root.dsl.input('test.textarea'); - chain.enter('foo'); - expect(_jQuery('textarea[ng-model="test.textarea"]').val()).toEqual('foo'); - }); - - it('should return error if no textarea exists', function() { - var chain = $root.dsl.input('test.textarea'); - chain.enter('foo'); - expect($root.futureError).toMatch(/did not match/); - }); - }); - }); -}); diff --git a/test/ngScenario/e2e/Runner-compiled.html b/test/ngScenario/e2e/Runner-compiled.html deleted file mode 100644 index 530fef96233a..000000000000 --- a/test/ngScenario/e2e/Runner-compiled.html +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/test/ngScenario/e2e/Runner.html b/test/ngScenario/e2e/Runner.html deleted file mode 100644 index f5d03ca5e4bc..000000000000 --- a/test/ngScenario/e2e/Runner.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/test/ngScenario/e2e/style.css b/test/ngScenario/e2e/style.css deleted file mode 100644 index 26540becebb7..000000000000 --- a/test/ngScenario/e2e/style.css +++ /dev/null @@ -1,11 +0,0 @@ -th { - text-align: left; -} - -tr { - border: 1px solid black; -} - -.redbox { - background-color: red; -} diff --git a/test/ngScenario/e2e/widgets-scenario.js b/test/ngScenario/e2e/widgets-scenario.js deleted file mode 100644 index 764347284f61..000000000000 --- a/test/ngScenario/e2e/widgets-scenario.js +++ /dev/null @@ -1,68 +0,0 @@ -/* global using: false, binding: false, input: false, select: false, repeater: false */ -'use strict'; - -describe('widgets', function() { - it('should verify that basic widgets work', function() { - browser().navigateTo('widgets.html'); - - using('#text-basic-box').input('text.basic').enter('Carlos'); - expect(binding('text.basic')).toEqual('Carlos'); - input('text.basic').enter('Carlos Santana'); - expect(binding('text.basic')).not().toEqual('Carlos Boozer'); - - input('text.password').enter('secret'); - expect(binding('text.password')).toEqual('secret'); - - expect(binding('text.hidden')).toEqual('hiddenValue'); - - expect(binding('gender')).toEqual('male'); - input('gender').select('female'); - expect(using('#gender-box').binding('gender')).toEqual('female'); - - expect(repeater('#repeater-row ul li').count()).toEqual(2); - expect(repeater('#repeater-row ul li').row(1)).toEqual(['adam']); - expect(repeater('#repeater-row ul li').column('name')).toEqual(['misko', 'adam']); - - select('select').option('B'); - expect(binding('select')).toEqual('B'); - - select('multiselect').options('A', 'C'); - expect(binding('multiselect').fromJson()).toEqual(['A', 'C']); - - expect(binding('button').fromJson()).toEqual({'count': 0}); - expect(binding('form').fromJson()).toEqual({'count': 0}); - - element('form a', "'action' link").click(); - expect(binding('button').fromJson()).toEqual({'count': 1}); - - element('input[value="submit input"]', "'submit input' button").click(); - expect(binding('button').fromJson()).toEqual({'count': 2}); - expect(binding('form').fromJson()).toEqual({'count': 1}); - - element('button:contains("submit button")', "'submit button' button").click(); - expect(binding('button').fromJson()).toEqual({'count': 2}); - expect(binding('form').fromJson()).toEqual({'count': 2}); - - element('input[value="button"]', "'button' button").click(); - expect(binding('button').fromJson()).toEqual({'count': 3}); - - element('input[type="image"]', 'form image').click(); - expect(binding('button').fromJson()).toEqual({'count': 4}); - - /** - * Custom value parser for futures. - */ - function checkboxParser(value) { - return angular.fromJson(value.substring(value.indexOf('=') + 1)); - } - - input('checkbox.tea').check(); - expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: false, tea: false}); - input('checkbox.coffee').check(); - expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: true, tea: false}); - input('checkbox.tea').check(); - input('checkbox.tea').check(); - input('checkbox.tea').check(); - expect(binding('checkbox').parsedWith(checkboxParser)).toEqual({coffee: true, tea: true}); - }); -}); diff --git a/test/ngScenario/e2e/widgets.html b/test/ngScenario/e2e/widgets.html deleted file mode 100644 index 6fa9276ed669..000000000000 --- a/test/ngScenario/e2e/widgets.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          DescriptionTestResult
          Input text field
          basic - - text.basic={{text.basic}}
          passwordtext.password={{text.password}}
          hiddentext.hidden={{text.hidden}}
          Input selection field
          radio - Female
          - Male -
          gender={{gender}}
          checkbox - Tea
          - Coffe -
          -
          checkbox={{checkbox}}
          -
          select - - select={{select}}
          multiselect - - multiselect={{multiselect}}
          Buttons
          ng-change
          ng-click
          -
          -
          -
          - -
          - action -
          -
          button={{button}} form={{form}}
          Repeaters
          ng-repeat -
            -
          • {{name}}
          • -
          -
          - - diff --git a/test/ngScenario/matchersSpec.js b/test/ngScenario/matchersSpec.js deleted file mode 100644 index cecba292690b..000000000000 --- a/test/ngScenario/matchersSpec.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -describe('angular.scenario.matchers', function() { - var matchers; - - function expectMatcher(value, test) { - delete matchers.error; - delete matchers.future.value; - if (value !== undefined) { - matchers.future.value = value; - } - test(); - expect(matchers.error).toBeUndefined(); - } - - beforeEach(function() { - /** - * Mock up the future system wrapped around matchers. - * - * @see Scenario.js#angular.scenario.matcher - */ - matchers = { - future: { name: 'test' } - }; - matchers.addFuture = function(name, callback) { - callback(function(error) { - matchers.error = error; - }); - }; - angular.extend(matchers, angular.scenario.matcher); - }); - - it('should handle basic matching', function() { - expectMatcher(10, function() { matchers.toEqual(10); }); - expectMatcher('value', function() { matchers.toBeDefined(); }); - expectMatcher([1], function() { matchers.toBeTruthy(); }); - expectMatcher("", function() { matchers.toBeFalsy(); }); - expectMatcher(0, function() { matchers.toBeFalsy(); }); - expectMatcher('foo', function() { matchers.toMatch('.o.'); }); - expectMatcher(null, function() { matchers.toBeNull(); }); - expectMatcher([1, 2, 3], function() { matchers.toContain(2); }); - expectMatcher(3, function() { matchers.toBeLessThan(10); }); - expectMatcher(3, function() { matchers.toBeGreaterThan(-5); }); - }); - - it('should have toHaveClass matcher', function() { - var e = angular.element('
          '); - expect(e).not.toHaveClass('none'); - expect(e).toHaveClass('abc'); - }); -}); diff --git a/test/ngScenario/mocks.js b/test/ngScenario/mocks.js deleted file mode 100644 index e135390f039d..000000000000 --- a/test/ngScenario/mocks.js +++ /dev/null @@ -1,33 +0,0 @@ -'use strict'; - -angular.scenario.testing = angular.scenario.testing || {}; - -angular.scenario.testing.MockAngular = function() { - this.reset(); - this.element = jqLite; -}; - -angular.scenario.testing.MockAngular.prototype.reset = function() { - this.log = []; -}; - -angular.scenario.testing.MockAngular.prototype.poll = function() { - this.log.push('$brower.poll()'); - return this; -}; - -angular.scenario.testing.MockRunner = function() { - this.listeners = []; -}; - -angular.scenario.testing.MockRunner.prototype.on = function(eventName, fn) { - this.listeners[eventName] = this.listeners[eventName] || []; - this.listeners[eventName].push(fn); -}; - -angular.scenario.testing.MockRunner.prototype.emit = function(eventName) { - var args = Array.prototype.slice.call(arguments, 1); - angular.forEach(this.listeners[eventName] || [], function(fn) { - fn.apply(this, args); - }); -}; diff --git a/test/ngScenario/output/HtmlSpec.js b/test/ngScenario/output/HtmlSpec.js deleted file mode 100644 index e5d6e9e78d42..000000000000 --- a/test/ngScenario/output/HtmlSpec.js +++ /dev/null @@ -1,127 +0,0 @@ -'use strict'; - -describe('angular.scenario.output.html', function() { - var runner, model, spec, step, listeners, ui, context; - - beforeEach(function() { - listeners = []; - spec = { - name: 'test spec', - definition: { - id: 10, - name: 'child', - children: [], - parent: { - id: 20, - name: 'parent', - children: [] - } - } - }; - step = { - name: 'some step', - line: function() { return 'unknown:-1'; } - }; - runner = new angular.scenario.testing.MockRunner(); - model = new angular.scenario.ObjectModel(runner); - context = _jQuery("
          "); - ui = angular.scenario.output.html(context, runner, model); - }); - - it('should create nested describe context', function() { - runner.emit('SpecBegin', spec); - expect(context.find('#describe-20 #describe-10 > h2').text()). - toEqual('describe: child'); - expect(context.find('#describe-20 > h2').text()).toEqual('describe: parent'); - expect(context.find('#describe-10 .tests > li .test-info .test-name').text()). - toEqual('test spec'); - expect(context.find('#describe-10 .tests > li').hasClass('status-pending')). - toBeTruthy(); - }); - - it('should add link on InteractivePause', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('StepBegin', spec, step); - runner.emit('InteractivePause', spec, step); - expect(context.find('.test-actions .test-title:first').text()).toEqual('some step'); - expect(lowercase(context.find('.test-actions .test-title:last').html())).toEqual( - 'paused... resume when ready.' - ); - }); - - it('should update totals when steps complete', function() { - // Failure - for (var i=0; i < 3; ++i) { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - } - - // Error - runner.emit('SpecBegin', spec); - runner.emit('SpecError', spec, 'error'); - runner.emit('SpecEnd', spec); - - // Error - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepError', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - // Success - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - expect(parseInt(context.find('#status-legend .status-failure').text(), 10)). - toEqual(3); - expect(parseInt(context.find('#status-legend .status-error').text(), 10)). - toEqual(2); - expect(parseInt(context.find('#status-legend .status-success').text(), 10)). - toEqual(1); - }); - - it('should update timer when test completes', function() { - // Success - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - // Failure - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - // Error - runner.emit('SpecBegin', spec); - runner.emit('SpecError', spec, 'error'); - runner.emit('SpecEnd', spec); - - context.find('#describe-10 .tests > li .test-info .timer-result'). - each(function(index, timer) { - expect(timer.innerHTML).toMatch(/ms$/); - } - ); - }); - - it('should include line if provided', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, 'error'); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - - var errorHtml = context.find('#describe-10 .tests li pre').html(); - expect(errorHtml.indexOf('unknown:-1')).toEqual(0); - }); - -}); diff --git a/test/ngScenario/output/jsonSpec.js b/test/ngScenario/output/jsonSpec.js deleted file mode 100644 index 06caf91cae99..000000000000 --- a/test/ngScenario/output/jsonSpec.js +++ /dev/null @@ -1,37 +0,0 @@ -'use strict'; - -describe('angular.scenario.output.json', function() { - var output, context; - var runner, model, $window; - var spec, step; - - beforeEach(function() { - $window = {}; - context = _jQuery('
          '); - runner = new angular.scenario.testing.MockRunner(); - model = new angular.scenario.ObjectModel(runner); - output = angular.scenario.output.json(context, runner, model); - spec = { - name: 'test spec', - definition: { - id: 10, - name: 'describe' - } - }; - step = { - name: 'some step', - line: function() { return 'unknown:-1'; } - }; - }); - - it('should put json in context on RunnerEnd', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - runner.emit('RunnerEnd'); - - expect(angular.fromJson(context.html()).children['describe'] - .specs['test spec'].status).toEqual('success'); - }); -}); diff --git a/test/ngScenario/output/objectSpec.js b/test/ngScenario/output/objectSpec.js deleted file mode 100644 index d92c939d4be2..000000000000 --- a/test/ngScenario/output/objectSpec.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -describe('angular.scenario.output.object', function() { - var output; - var runner, model, $window; - var spec, step; - - beforeEach(function() { - $window = {}; - runner = new angular.scenario.testing.MockRunner(); - model = new angular.scenario.ObjectModel(runner); - runner.$window = $window; - output = angular.scenario.output.object(null, runner, model); - spec = { - name: 'test spec', - definition: { - id: 10, - name: 'describe', - children: [] - } - }; - step = { - name: 'some step', - line: function() { return 'unknown:-1'; } - }; - }); - - it('should create a global variable $result', function() { - expect($window.$result).toBeDefined(); - }); - - it('should maintain live state in $result', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - - expect($window.$result.children['describe'] - .specs['test spec'].steps[0].duration).toBeDefined(); - }); -}); diff --git a/test/ngScenario/output/xmlSpec.js b/test/ngScenario/output/xmlSpec.js deleted file mode 100644 index 32646417cb1d..000000000000 --- a/test/ngScenario/output/xmlSpec.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -describe('angular.scenario.output.xml', function() { - var output, context; - var runner, model, $window; - var spec, step; - - beforeEach(function() { - $window = {}; - context = _jQuery('
          '); - runner = new angular.scenario.testing.MockRunner(); - model = new angular.scenario.ObjectModel(runner); - output = angular.scenario.output.xml(context, runner, model); - spec = { - name: 'test spec', - definition: { - id: 10, - name: 'describe' - } - }; - step = { - name: 'some step', - line: function() { return 'unknown:-1'; } - }; - }); - - it('should create XML nodes for object model', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - runner.emit('RunnerEnd'); - expect(context.find('it').attr('status')).toEqual('success'); - expect(context.find('it step').attr('status')).toEqual('success'); - }); - - it('should output errors to the XML', function() { - runner.emit('SpecBegin', spec); - runner.emit('StepBegin', spec, step); - runner.emit('StepFailure', spec, step, 'error reason'); - runner.emit('StepEnd', spec, step); - runner.emit('SpecEnd', spec); - runner.emit('RunnerEnd'); - - expect(context.find('it').attr('status')).toEqual('failure'); - expect(context.find('it step').attr('status')).toEqual('failure'); - expect(context.find('it step').text()).toEqual('error reason'); - }); -}); diff --git a/test/ngTouch/directive/ngClickSpec.js b/test/ngTouch/directive/ngClickSpec.js deleted file mode 100644 index 921c64578b2b..000000000000 --- a/test/ngTouch/directive/ngClickSpec.js +++ /dev/null @@ -1,604 +0,0 @@ -'use strict'; - -describe('ngClick (touch)', function() { - var element, time, orig_now; - - // TODO(braden): Once we have other touch-friendly browsers on CI, allow them here. - // Currently Firefox and IE refuse to fire touch events. - var chrome = /chrome/.test(navigator.userAgent.toLowerCase()); - if (!chrome) { - return; - } - - function mockTime() { - return time; - } - - - beforeEach(function() { - module('ngTouch'); - orig_now = Date.now; - time = 0; - Date.now = mockTime; - }); - - afterEach(function() { - dealoc(element); - Date.now = orig_now; - }); - - - it('should get called on a tap', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - $rootScope.$digest(); - expect($rootScope.tapped).toBeUndefined(); - - browserTrigger(element, 'touchstart'); - browserTrigger(element, 'touchend'); - expect($rootScope.tapped).toEqual(true); - })); - - - it('should pass event object', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - $rootScope.$digest(); - - browserTrigger(element, 'touchstart'); - browserTrigger(element, 'touchend'); - expect($rootScope.event).toBeDefined(); - })); - - - it('should not click if the touch is held too long', inject(function($rootScope, $compile, $rootElement) { - element = $compile('
          ')($rootScope); - $rootElement.append(element); - $rootScope.count = 0; - $rootScope.$digest(); - - expect($rootScope.count).toBe(0); - - time = 10; - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - - time = 900; - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count).toBe(0); - })); - - - it('should not click if the touchend is too far away', inject(function($rootScope, $compile, $rootElement) { - element = $compile('
          ')($rootScope); - $rootElement.append(element); - $rootScope.$digest(); - - expect($rootScope.tapped).toBeUndefined(); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 400, - y: 400 - }); - - expect($rootScope.tapped).toBeUndefined(); - })); - - - it('should not click if a touchmove comes before touchend', inject(function($rootScope, $compile, $rootElement) { - element = $compile('
          ')($rootScope); - $rootElement.append(element); - $rootScope.$digest(); - - expect($rootScope.tapped).toBeUndefined(); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchmove'); - browserTrigger(element, 'touchend',{ - keys: [], - x: 400, - y: 400 - }); - - expect($rootScope.tapped).toBeUndefined(); - })); - - it('should add the CSS class while the element is held down, and then remove it', inject(function($rootScope, $compile, $rootElement) { - element = $compile('
          ')($rootScope); - $rootElement.append(element); - $rootScope.$digest(); - expect($rootScope.tapped).toBeUndefined(); - - var CSS_CLASS = 'ng-click-active'; - - expect(element.hasClass(CSS_CLASS)).toBe(false); - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - expect(element.hasClass(CSS_CLASS)).toBe(true); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - expect(element.hasClass(CSS_CLASS)).toBe(false); - expect($rootScope.tapped).toBe(true); - })); - - - describe('the clickbuster', function() { - var element1, element2; - - beforeEach(inject(function($rootElement, $document) { - $document.find('body').append($rootElement); - })); - - afterEach(inject(function($document) { - $document.find('body').empty(); - })); - - - it('should cancel the following click event', inject(function($rootScope, $compile, $rootElement, $document) { - element = $compile('
          ')($rootScope); - $rootElement.append(element); - - $rootScope.count = 0; - $rootScope.$digest(); - - expect($rootScope.count).toBe(0); - - // Fire touchstart at 10ms, touchend at 50ms, the click at 300ms. - time = 10; - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - - time = 50; - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count).toBe(1); - - time = 100; - browserTrigger(element, 'click',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count).toBe(1); - })); - - - it('should cancel the following click event even when the element has changed', inject( - function($rootScope, $compile, $rootElement) { - $rootElement.append( - '
          x
          ' + - '
          y
          ' - ); - $compile($rootElement)($rootScope); - - element1 = $rootElement.find('div').eq(0); - element2 = $rootElement.find('div').eq(1); - - $rootScope.count1 = 0; - $rootScope.count2 = 0; - - $rootScope.$digest(); - - expect($rootScope.count1).toBe(0); - expect($rootScope.count2).toBe(0); - - time = 10; - browserTrigger(element1, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - - time = 50; - browserTrigger(element1, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count1).toBe(1); - - time = 100; - browserTrigger(element2, 'click',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count1).toBe(1); - expect($rootScope.count2).toBe(0); - })); - - - it('should not cancel clicks on distant elements', inject(function($rootScope, $compile, $rootElement) { - $rootElement.append( - '
          x
          ' + - '
          y
          ' - ); - $compile($rootElement)($rootScope); - - element1 = $rootElement.find('div').eq(0); - element2 = $rootElement.find('div').eq(1); - - $rootScope.count1 = 0; - $rootScope.count2 = 0; - - $rootScope.$digest(); - - expect($rootScope.count1).toBe(0); - expect($rootScope.count2).toBe(0); - - time = 10; - browserTrigger(element1, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - - time = 50; - browserTrigger(element1, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count1).toBe(1); - - time = 90; - // Verify that it is blured so we don't get soft-keyboard - element1[0].blur = jasmine.createSpy('blur'); - browserTrigger(element1, 'click',{ - keys: [], - x: 10, - y: 10 - }); - expect(element1[0].blur).toHaveBeenCalled(); - - expect($rootScope.count1).toBe(1); - - time = 100; - browserTrigger(element1, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - - time = 130; - browserTrigger(element1, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count1).toBe(2); - - // Click on other element that should go through. - time = 150; - browserTrigger(element2, 'touchstart',{ - keys: [], - x: 100, - y: 120 - }); - browserTrigger(element2, 'touchend',{ - keys: [], - x: 100, - y: 120 - }); - browserTrigger(element2, 'click',{ - keys: [], - x: 100, - y: 120 - }); - - expect($rootScope.count2).toBe(1); - - // Click event for the element that should be busted. - time = 200; - browserTrigger(element1, 'click',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count1).toBe(2); - expect($rootScope.count2).toBe(1); - })); - - - it('should not cancel clicks that come long after', inject(function($rootScope, $compile) { - element1 = $compile('
          ')($rootScope); - - $rootScope.count = 0; - - $rootScope.$digest(); - - expect($rootScope.count).toBe(0); - - time = 10; - browserTrigger(element1, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - - time = 50; - browserTrigger(element1, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - expect($rootScope.count).toBe(1); - - time = 2700; - browserTrigger(element1, 'click',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.count).toBe(2); - })); - - - describe('when clicking on a label immediately following a touch event', function() { - var touch = function(element, x, y) { - time = 10; - browserTrigger(element, 'touchstart',{ - keys: [], - x: x, - y: y - }); - - time = 50; - browserTrigger(element, 'touchend',{ - keys: [], - x: x, - y: y - }); - }; - - var click = function(element, x, y) { - browserTrigger(element, 'click',{ - keys: [], - x: x, - y: y - }); - }; - - var $rootScope; - var container, otherElement, input, label; - beforeEach(inject(function(_$rootScope_, $compile, $rootElement) { - $rootScope = _$rootScope_; - var container = $compile('
          ' + - '' + - '
          ')($rootScope); - $rootElement.append(container); - otherElement = container.children()[0]; - input = container.children()[1]; - label = container.children()[2]; - - $rootScope.selection = 'initial'; - - $rootScope.$digest(); - })); - - - afterEach(function() { - dealoc(label); - dealoc(input); - dealoc(otherElement); - dealoc(container); - }); - - - it('should not cancel input clicks with (0,0) coordinates', function() { - touch(otherElement, 100, 100); - - time = 500; - click(label, 10, 10); - click(input, 0, 0); - - expect($rootScope.selection).toBe('radio1'); - }); - - - it('should not cancel input clicks with negative coordinates', function() { - touch(otherElement, 100, 100); - - time = 500; - click(label, 10, 10); - click(input, -1, -1); - - expect($rootScope.selection).toBe('radio1'); - }); - - - it('should not cancel input clicks with positive coordinates identical to label click', function() { - touch(otherElement, 100, 100); - - time = 500; - click(label, 10, 10); - click(input, 10, 10); - - expect($rootScope.selection).toBe('radio1'); - }); - - - it('should cancel input clicks with positive coordinates different than label click', function() { - touch(otherElement, 100, 100); - - time = 500; - click(label, 10, 10); - click(input, 11, 11); - - expect($rootScope.selection).toBe('initial'); - }); - }); - }); - - - describe('click fallback', function() { - - it('should treat a click as a tap on desktop', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - $rootScope.$digest(); - expect($rootScope.tapped).toBeFalsy(); - - browserTrigger(element, 'click'); - expect($rootScope.tapped).toEqual(true); - })); - - - it('should pass event object', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - $rootScope.$digest(); - - browserTrigger(element, 'click'); - expect($rootScope.event).toBeDefined(); - })); - }); - - - describe('disabled state', function() { - it('should not trigger click if ngDisabled is true', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - $rootScope.disabled = true; - $rootScope.$digest(); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.event).toBeUndefined(); - })); - it('should trigger click if ngDisabled is false', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - $rootScope.disabled = false; - $rootScope.$digest(); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.event).toBeDefined(); - })); - it('should not trigger click if regular disabled is true', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.event).toBeUndefined(); - })); - it('should not trigger click if regular disabled is present', inject(function($rootScope, $compile) { - element = $compile('')($rootScope); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.event).toBeUndefined(); - })); - it('should trigger click if regular disabled is not present', inject(function($rootScope, $compile) { - element = $compile('
          ')($rootScope); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect($rootScope.event).toBeDefined(); - })); - }); - - - describe('the normal click event', function() { - it('should be capturable by other handlers', inject(function($rootScope, $compile) { - var called = false; - - element = $compile('
          ')($rootScope); - - element.on('click', function() { - called = true; - }); - - browserTrigger(element, 'touchstart',{ - keys: [], - x: 10, - y: 10 - }); - browserTrigger(element, 'touchend',{ - keys: [], - x: 10, - y: 10 - }); - - expect(called).toEqual(true); - })); - }); -}); diff --git a/test/ngTouch/directive/ngSwipeSpec.js b/test/ngTouch/directive/ngSwipeSpec.js index 24ad0d76dc20..a2ba563c9903 100644 --- a/test/ngTouch/directive/ngSwipeSpec.js +++ b/test/ngTouch/directive/ngSwipeSpec.js @@ -8,7 +8,7 @@ var swipeTests = function(description, restrictBrowsers, startEvent, moveEvent, if (restrictBrowsers) { // TODO(braden): Once we have other touch-friendly browsers on CI, allow them here. // Currently Firefox and IE refuse to fire touch events. - var chrome = /chrome/.test(navigator.userAgent.toLowerCase()); + var chrome = /chrome/.test(window.navigator.userAgent.toLowerCase()); if (!chrome) { return; } @@ -232,6 +232,6 @@ var swipeTests = function(description, restrictBrowsers, startEvent, moveEvent, }); }; -swipeTests('touch', /* restrictBrowers */ true, 'touchstart', 'touchmove', 'touchend'); -swipeTests('mouse', /* restrictBrowers */ false, 'mousedown', 'mousemove', 'mouseup'); +swipeTests('touch', /* restrictBrowsers */ true, 'touchstart', 'touchmove', 'touchend'); +swipeTests('mouse', /* restrictBrowsers */ false, 'mousedown', 'mousemove', 'mouseup'); diff --git a/test/ngTouch/swipeSpec.js b/test/ngTouch/swipeSpec.js index 416d9f5d632d..ae54316308a8 100644 --- a/test/ngTouch/swipeSpec.js +++ b/test/ngTouch/swipeSpec.js @@ -25,18 +25,19 @@ describe('$swipe', function() { var usedEvents; var MOUSE_EVENTS = ['mousedown','mousemove','mouseup'].sort(); var TOUCH_EVENTS = ['touchcancel','touchend','touchmove','touchstart'].sort(); - var ALL_EVENTS = MOUSE_EVENTS.concat(TOUCH_EVENTS).sort(); + var POINTER_EVENTS = ['pointerdown', 'pointermove', 'pointerup', 'pointercancel'].sort(); + var ALL_EVENTS = MOUSE_EVENTS.concat(TOUCH_EVENTS, POINTER_EVENTS).sort(); beforeEach(function() { usedEvents = []; - spyOn(element, 'on').andCallFake(function(events) { + spyOn(element, 'on').and.callFake(function(events) { angular.forEach(events.split(/\s+/), function(eventName) { usedEvents.push(eventName); }); }); }); - it('should use mouse and touch by default', inject(function($swipe) { + it('should use mouse, touch and pointer by default', inject(function($swipe) { $swipe.bind(element, events); expect(usedEvents.sort()).toEqual(ALL_EVENTS); })); @@ -51,15 +52,36 @@ describe('$swipe', function() { expect(usedEvents.sort()).toEqual(TOUCH_EVENTS); })); + it('should only use pointer events for pointerType "pointer"', inject(function($swipe) { + $swipe.bind(element, events, ['pointer']); + expect(usedEvents.sort()).toEqual(POINTER_EVENTS); + })); + it('should use mouse and touch if both are specified', inject(function($swipe) { $swipe.bind(element, events, ['touch', 'mouse']); + expect(usedEvents.sort()).toEqual(MOUSE_EVENTS.concat(TOUCH_EVENTS).sort()); + })); + + it('should use mouse and pointer if both are specified', inject(function($swipe) { + $swipe.bind(element, events, ['mouse', 'pointer']); + expect(usedEvents.sort()).toEqual(MOUSE_EVENTS.concat(POINTER_EVENTS).sort()); + })); + + it('should use touch and pointer if both are specified', inject(function($swipe) { + $swipe.bind(element, events, ['touch', 'pointer']); + expect(usedEvents.sort()).toEqual(TOUCH_EVENTS.concat(POINTER_EVENTS).sort()); + })); + + it('should use mouse, touch and pointer if they are specified', inject(function($swipe) { + $swipe.bind(element, events, ['mouse', 'touch', 'pointer']); expect(usedEvents.sort()).toEqual(ALL_EVENTS); })); }); - swipeTests('touch', /* restrictBrowers */ true, 'touchstart', 'touchmove', 'touchend'); - swipeTests('mouse', /* restrictBrowers */ false, 'mousedown', 'mousemove', 'mouseup'); + swipeTests('touch', /* restrictBrowsers */ true, 'touchstart', 'touchmove', 'touchend'); + swipeTests('pointer', /* restrictBrowsers */ true, 'pointerdown', 'pointermove', 'pointerup'); + swipeTests('mouse', /* restrictBrowsers */ false, 'mousedown', 'mousemove', 'mouseup'); // Wrapper to abstract over using touch events or mouse events. function swipeTests(description, restrictBrowsers, startEvent, moveEvent, endEvent) { @@ -67,8 +89,8 @@ describe('$swipe', function() { if (restrictBrowsers) { // TODO(braden): Once we have other touch-friendly browsers on CI, allow them here. // Currently Firefox and IE refuse to fire touch events. - var chrome = /chrome/.test(navigator.userAgent.toLowerCase()); - if (!chrome) { + // Enable iPhone for manual testing. + if (!/chrome|iphone/i.test(window.navigator.userAgent)) { return; } } @@ -224,7 +246,7 @@ describe('$swipe', function() { }); expect(events.start).toHaveBeenCalled(); - expect(events.move.calls.length).toBe(7); + expect(events.move).toHaveBeenCalledTimes(7); expect(events.cancel).not.toHaveBeenCalled(); expect(events.end).not.toHaveBeenCalled(); @@ -236,7 +258,7 @@ describe('$swipe', function() { }); expect(events.start).toHaveBeenCalled(); - expect(events.move.calls.length).toBe(7); + expect(events.move).toHaveBeenCalledTimes(7); expect(events.end).toHaveBeenCalled(); expect(events.cancel).not.toHaveBeenCalled(); @@ -289,7 +311,7 @@ describe('$swipe', function() { }); expect(events.start).toHaveBeenCalled(); - expect(events.move.calls.length).toBe(3); + expect(events.move).toHaveBeenCalledTimes(3); expect(events.cancel).not.toHaveBeenCalled(); expect(events.end).not.toHaveBeenCalled(); @@ -301,7 +323,7 @@ describe('$swipe', function() { }); expect(events.start).toHaveBeenCalled(); - expect(events.move.calls.length).toBe(3); + expect(events.move).toHaveBeenCalledTimes(3); expect(events.end).toHaveBeenCalled(); expect(events.cancel).not.toHaveBeenCalled(); diff --git a/test/stringifySpec.js b/test/stringifySpec.js index e849b3e86cb8..a9b0c2c30202 100644 --- a/test/stringifySpec.js +++ b/test/stringifySpec.js @@ -9,7 +9,46 @@ describe('toDebugString', function() { expect(toDebugString()).toEqual('undefined'); var a = { }; a.a = a; - expect(toDebugString(a)).toEqual('{"a":"<>"}'); - expect(toDebugString([a,a])).toEqual('[{"a":"<>"},"<>"]'); + expect(toDebugString(a)).toEqual('{"a":"..."}'); + expect(toDebugString([a,a])).toEqual('[{"a":"..."},"..."]'); }); + + it('should convert its argument that are objects to string based on maxDepth', function() { + var a = {b: {c: {d: 1}}}; + expect(toDebugString(a, 1)).toEqual('{"b":"..."}'); + expect(toDebugString(a, 2)).toEqual('{"b":{"c":"..."}}'); + expect(toDebugString(a, 3)).toEqual('{"b":{"c":{"d":1}}}'); + }); + + they('should convert its argument that object to string and ignore max depth when maxDepth = $prop', + [NaN, null, undefined, true, false, -1, 0], function(maxDepth) { + var a = {b: {c: {d: 1}}}; + expect(toDebugString(a, maxDepth)).toEqual('{"b":{"c":{"d":1}}}'); + } + ); +}); + +describe('serializeObject', function() { + it('should convert its argument to a string', function() { + expect(serializeObject({a:{b:'c'}})).toEqual('{"a":{"b":"c"}}'); + + var a = { }; + a.a = a; + expect(serializeObject(a)).toEqual('{"a":"..."}'); + expect(serializeObject([a,a])).toEqual('[{"a":"..."},"..."]'); + }); + + it('should convert its argument that are objects to string based on maxDepth', function() { + var a = {b: {c: {d: 1}}}; + expect(serializeObject(a, 1)).toEqual('{"b":"..."}'); + expect(serializeObject(a, 2)).toEqual('{"b":{"c":"..."}}'); + expect(serializeObject(a, 3)).toEqual('{"b":{"c":{"d":1}}}'); + }); + + they('should convert its argument that object to string and ignore max depth when maxDepth = $prop', + [NaN, null, undefined, true, false, -1, 0], function(maxDepth) { + var a = {b: {c: {d: 1}}}; + expect(serializeObject(a, maxDepth)).toEqual('{"b":{"c":{"d":1}}}'); + } + ); }); diff --git a/validate-commit-msg.js b/validate-commit-msg.js deleted file mode 100755 index f0c23fda950a..000000000000 --- a/validate-commit-msg.js +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env node - -/** - * Git COMMIT-MSG hook for validating commit message - * See https://docs.google.com/document/d/1rk04jEuGfk9kYzfqCuOlPTSJw3hEDZJTBN5E5f1SALo/edit - * - * Installation: - * >> cd - * >> ln -s ../../validate-commit-msg.js .git/hooks/commit-msg - */ - -'use strict'; - -var fs = require('fs'); -var util = require('util'); - - -var MAX_LENGTH = 100; -var PATTERN = /^(?:fixup!\s*)?(\w*)(\(([\w\$\.\*/-]*)\))?\: (.*)$/; -var IGNORED = /^WIP\:/; -var TYPES = { - feat: true, - fix: true, - docs: true, - style: true, - refactor: true, - perf: true, - test: true, - chore: true, - revert: true -}; - - -var error = function() { - // gitx does not display it - // http://gitx.lighthouseapp.com/projects/17830/tickets/294-feature-display-hook-error-message-when-hook-fails - // https://groups.google.com/group/gitx/browse_thread/thread/a03bcab60844b812 - console.error('INVALID COMMIT MSG: ' + util.format.apply(null, arguments)); -}; - - -var validateMessage = function(message) { - var isValid = true; - - if (IGNORED.test(message)) { - console.log('Commit message validation ignored.'); - return true; - } - - if (message.length > MAX_LENGTH) { - error('is longer than %d characters !', MAX_LENGTH); - isValid = false; - } - - var match = PATTERN.exec(message); - - if (!match) { - error('does not match "(): " ! was: ' + message); - return false; - } - - var type = match[1]; - var scope = match[3]; - var subject = match[4]; - - if (!TYPES.hasOwnProperty(type)) { - error('"%s" is not allowed type !', type); - return false; - } - - // Some more ideas, do want anything like this ? - // - allow only specific scopes (eg. fix(docs) should not be allowed ? - // - auto correct the type to lower case ? - // - auto correct first letter of the subject to lower case ? - // - auto add empty line after subject ? - // - auto remove empty () ? - // - auto correct typos in type ? - // - store incorrect messages, so that we can learn - - return isValid; -}; - - -var firstLineFromBuffer = function(buffer) { - return buffer.toString().split('\n').shift(); -}; - - - -// publish for testing -exports.validateMessage = validateMessage; - -// hacky start if not run by jasmine :-D -if (process.argv.join('').indexOf('jasmine-node') === -1) { - var commitMsgFile = process.argv[2]; - var incorrectLogFile = commitMsgFile.replace('COMMIT_EDITMSG', 'logs/incorrect-commit-msgs'); - - fs.readFile(commitMsgFile, function(err, buffer) { - var msg = firstLineFromBuffer(buffer); - - if (!validateMessage(msg)) { - fs.appendFile(incorrectLogFile, msg + '\n', function() { - process.exit(1); - }); - } else { - process.exit(0); - } - }); -} diff --git a/validate-commit-msg.spec.js b/validate-commit-msg.spec.js deleted file mode 100644 index 968ee048fd72..000000000000 --- a/validate-commit-msg.spec.js +++ /dev/null @@ -1,80 +0,0 @@ -/* global describe: false, beforeEach: false, it: false, expect: false, spyOn: false */ -'use strict'; - -describe('validate-commit-msg.js', function() { - var m = require('./validate-commit-msg'); - var errors = []; - var logs = []; - - var VALID = true; - var INVALID = false; - - beforeEach(function() { - errors.length = 0; - logs.length = 0; - - spyOn(console, 'error').andCallFake(function(msg) { - errors.push(msg.replace(/\x1B\[\d+m/g, '')); // uncolor - }); - - spyOn(console, 'log').andCallFake(function(msg) { - logs.push(msg.replace(/\x1B\[\d+m/g, '')); // uncolor - }); - }); - - describe('validateMessage', function() { - - it('should be valid', function() { - expect(m.validateMessage('fixup! fix($compile): something')).toBe(VALID); - expect(m.validateMessage('fix($compile): something')).toBe(VALID); - expect(m.validateMessage('feat($location): something')).toBe(VALID); - expect(m.validateMessage('docs($filter): something')).toBe(VALID); - expect(m.validateMessage('style($http): something')).toBe(VALID); - expect(m.validateMessage('refactor($httpBackend): something')).toBe(VALID); - expect(m.validateMessage('test($resource): something')).toBe(VALID); - expect(m.validateMessage('chore($controller): something')).toBe(VALID); - expect(m.validateMessage('chore(foo-bar): something')).toBe(VALID); - expect(m.validateMessage('chore(*): something')).toBe(VALID); - expect(m.validateMessage('chore(guide/location): something')).toBe(VALID); - expect(m.validateMessage('revert(foo): something')).toBe(VALID); - expect(errors).toEqual([]); - }); - - - it('should validate 100 characters length', function() { - var msg = "fix($compile): something super mega extra giga tera long, maybe even longer and longer and longer... "; - - expect(m.validateMessage(msg)).toBe(INVALID); - expect(errors).toEqual(['INVALID COMMIT MSG: is longer than 100 characters !']); - }); - - - it('should validate "(): " format', function() { - var msg = 'not correct format'; - - expect(m.validateMessage(msg)).toBe(INVALID); - expect(errors).toEqual(['INVALID COMMIT MSG: does not match "(): " ! was: not correct format']); - }); - - - it('should validate type', function() { - expect(m.validateMessage('weird($filter): something')).toBe(INVALID); - expect(errors).toEqual(['INVALID COMMIT MSG: "weird" is not allowed type !']); - }); - - - it('should allow empty scope', function() { - expect(m.validateMessage('fix: blablabla')).toBe(VALID); - }); - - - it('should allow dot in scope', function() { - expect(m.validateMessage('chore(mocks.$httpBackend): something')).toBe(VALID); - }); - - - it('should ignore msg prefixed with "WIP: "', function() { - expect(m.validateMessage('WIP: bullshit')).toBe(VALID); - }); - }); -}); diff --git a/vendor/README.md b/vendor/README.md new file mode 100644 index 000000000000..959edac8f6f9 --- /dev/null +++ b/vendor/README.md @@ -0,0 +1,27 @@ +# Vendor Libraries + +Libraries that are not available via yarn / the npm registry, are checked into git in the `vendor` +folder. + +Currently this affects the following libraries: + +## closure-compiler + +Version: `20140814` + +The closure-compiler is available on npm, but not the version we are using. + +## ng-closure-runner + +Version: `0.2.4` + +This project has never been published to npm. + +# Updating the libraries + +If a different version becomes available, it must be manually downloaded and replaced in the +repository. + +Should yarn support requiring zip archives in the future (see +[yarn github issue: Zip support](https://github.com/yarnpkg/yarn/issues/1483)), +we can remove the libraries from the repository and add them to the package.json. diff --git a/vendor/closure-compiler/COPYING b/vendor/closure-compiler/COPYING new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/vendor/closure-compiler/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/closure-compiler/README.md b/vendor/closure-compiler/README.md new file mode 100644 index 000000000000..7e024e74017a --- /dev/null +++ b/vendor/closure-compiler/README.md @@ -0,0 +1,530 @@ +# [Google Closure Compiler](https://developers.google.com/closure/compiler/) + +[![Build Status](https://travis-ci.org/google/closure-compiler.svg?branch=master)](https://travis-ci.org/google/closure-compiler) + +The [Closure Compiler](https://developers.google.com/closure/compiler/) is a tool for making JavaScript download and run faster. It is a true compiler for JavaScript. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what's left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls. + +## Getting Started + * [Download the latest version](http://dl.google.com/closure-compiler/compiler-latest.zip) + * See the [Google Developers Site](https://developers.google.com/closure/compiler/docs/gettingstarted_app) for documentation including instructions for running the compiler from the command line. + +## Options for Getting Help +1. Post in the [Closure Compiler Discuss Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss) +2. Ask a question on [Stack Overflow](http://stackoverflow.com/questions/tagged/google-closure-compiler) +3. Consult the [FAQ](https://github.com/google/closure-compiler/wiki/FAQ) + +## Building it Yourself + +Note: The Closure Compiler requires [Java 7 or higher](http://www.java.com/). + +### Using [Ant](http://ant.apache.org/) + +1. Download the [Ant build tool](http://ant.apache.org/bindownload.cgi). + +2. At the root of the source tree, there is an Ant file named ```build.xml```. + To use it, navigate to the same directory and type the command + + ``` + ant jar + ``` + + This will produce a jar file called ```build/compiler.jar```. + +### Using [Eclipse](http://www.eclipse.org/) + +1. Download and open the [Eclipse IDE](http://www.eclipse.org/). +2. Navigate to ```File > New > Project ...``` and create a Java Project. Give + the project a name. +3. Select ```Create project from existing source``` and choose the root of the + checked-out source tree as the existing directory. +3. Navigate to the ```build.xml``` file. You will see all the build rules in + the Outline pane. Run the ```jar``` rule to build the compiler in + ```build/compiler.jar```. + +## Running + +On the command line, at the root of this project, type + +``` +java -jar build/compiler.jar +``` + +This starts the compiler in interactive mode. Type + +```javascript +var x = 17 + 25; +``` + +then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) +and "Enter" again. The Compiler will respond: + +```javascript +var x=42; +``` + +The Closure Compiler has many options for reading input from a file, writing +output to a file, checking your code, and running optimizations. To learn more, +type + +``` +java -jar compiler.jar --help +``` + +More detailed information about running the Closure Compiler is available in the +[documentation](http://code.google.com/closure/compiler/docs/gettingstarted_app.html). + +## Compiling Multiple Scripts + +If you have multiple scripts, you should compile them all together with one +compile command. + +```bash +java -jar compiler.jar --js_output_file=out.js in1.js in2.js in3.js ... +``` + +You can also use minimatch-style globs. + +```bash +# Recursively include all js files in subdirs +java -jar compiler.jar --js_output_file=out.js 'src/**.js' + +# Recursively include all js files in subdirs, exclusing test files. +# Use single-quotes, so that bash doesn't try to expand the '!' +java -jar compiler.jar --js_output_file=out.js 'src/**.js' '!**_test.js' +``` + +The Closure Compiler will concatenate the files in the order they're passed at +the command line. + +If you're using globs or many files, you may start to run into +problems with managing dependencies between scripts. In this case, you should +use the [Closure Library](https://developers.google.com/closure/library/). It +contains functions for enforcing dependencies between scripts, and Closure Compiler +will re-order the inputs automatically. + +## How to Contribute +### Reporting a bug +1. First make sure that it is really a bug and not simply the way that Closure Compiler works (especially true for ADVANCED_OPTIMIZATIONS). + * Check the [official documentation](https://developers.google.com/closure/compiler/) + * Consult the [FAQ](https://github.com/google/closure-compiler/wiki/FAQ) + * Search on [Stack Overflow](http://stackoverflow.com/questions/tagged/google-closure-compiler) and in the [Closure Compiler Discuss Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss) +2. If you still think you have found a bug, make sure someone hasn't already reported it. See the list of [known issues](https://github.com/google/closure-compiler/issues). +3. If it hasn't been reported yet, post a new issue. Make sure to add enough detail so that the bug can be recreated. The smaller the reproduction code, the better. + +### Suggesting a Feature +1. Consult the [FAQ](https://github.com/google/closure-compiler/wiki/FAQ) to make sure that the behaviour you would like isn't specifically excluded (such as string inlining). +2. Make sure someone hasn't requested the same thing. See the list of [known issues](https://github.com/google/closure-compiler/issues). +3. Read up on [what type of feature requests are accepted](https://github.com/google/closure-compiler/wiki/FAQ#how-do-i-submit-a-feature-request-for-a-new-type-of-optimization). +4. Submit your reqest as an issue. + +### Submitting patches +1. All contributors must sign a contributor license agreement. See the [CONTRIBUTORS](https://raw.githubusercontent.com/google/closure-compiler/master/CONTRIBUTORS) file for details. +2. To make sure your changes are of the type that will be accepted, ask about your patch on the [Closure Compiler Discuss Group](https://groups.google.com/forum/#!forum/closure-compiler-discuss) +3. Fork the repository. +4. Make your changes. +5. Submit a pull request for your changes. A project developer will review your work and then merge your request into the project. + +## Closure Compiler License + +Copyright 2009 The Closure Compiler Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +## Dependency Licenses + +### Rhino + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Path + src/com/google/javascript/rhino, test/com/google/javascript/rhino +
          URLhttp://www.mozilla.org/rhino
          Version1.5R3, with heavy modifications
          LicenseNetscape Public License and MPL / GPL dual license
          DescriptionA partial copy of Mozilla Rhino. Mozilla Rhino is an +implementation of JavaScript for the JVM. The JavaScript +parse tree data structures were extracted and modified +significantly for use by Google's JavaScript compiler.
          Local ModificationsThe packages have been renamespaced. All code not +relevant to the parse tree has been removed. A JsDoc parser and static typing +system have been added.
          + +### Args4j + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/args4j.jar
          URLhttps://args4j.dev.java.net/
          Version2.0.26
          LicenseMIT
          Descriptionargs4j is a small Java class library that makes it easy to parse command line +options/arguments in your CUI application.
          Local ModificationsNone
          + +### Guava Libraries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/guava.jar
          URLhttp://code.google.com/p/guava-libraries/
          Version17.0
          LicenseApache License 2.0
          DescriptionGoogle's core Java libraries.
          Local ModificationsNone
          + +### JSR 305 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/jsr305.jar
          URLhttp://code.google.com/p/jsr-305/
          Versionsvn revision 47
          LicenseBSD License
          DescriptionAnnotations for software defect detection.
          Local ModificationsNone
          + +### JUnit + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/junit.jar
          URLhttp://sourceforge.net/projects/junit/
          Version4.11
          LicenseCommon Public License 1.0
          DescriptionA framework for writing and running automated tests in Java.
          Local ModificationsNone
          + +### Protocol Buffers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/protobuf-java.jar
          URLhttp://code.google.com/p/protobuf/
          Version2.5.0
          LicenseNew BSD License
          DescriptionSupporting libraries for protocol buffers, +an encoding of structured data.
          Local ModificationsNone
          + +### Ant + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Path + lib/ant.jar, lib/ant-launcher.jar +
          URLhttp://ant.apache.org/bindownload.cgi
          Version1.8.1
          LicenseApache License 2.0
          DescriptionAnt is a Java based build tool. In theory it is kind of like "make" +without make's wrinkles and with the full portability of pure java code.
          Local ModificationsNone
          + +### JSON + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/json.jar
          URLhttp://json.org/java/index.html
          VersionJSON version 20090211
          LicenseMIT license
          DescriptionJSON is a set of java files for use in transmitting data in JSON format.
          Local ModificationsNone
          + +### Mockito + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/mockito-core.jar
          URLhttps://code.google.com/p/mockito
          Version1.9.5
          LicenseMIT license
          DescriptionMockito is an open source testing framework for Java. The framework allows the +creation of Test Double objects (called "Mock Objects") in automated unit tests +for the purpose of Test-driven Development (TDD) or Behavior Driven Development +(BDD).
          Local ModificationsNone
          + +### Objenesis + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathlib/objenesis.jar
          URLhttp://objenesis.org
          Version1.2
          LicenseApache 2.0 license
          DescriptionDepended by lib/mockito-core.jar, not used directly.
          Local ModificationsNone
          + +### Node.js Closure Compiler Externs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
          Code Pathcontrib/nodejs
          URLhttps://github.com/dcodeIO/node.js-closure-compiler-externs
          Versione891b4fbcf5f466cc4307b0fa842a7d8163a073a
          LicenseApache 2.0 license
          DescriptionType contracts for NodeJS APIs
          Local ModificationsSubstantial changes to make them compatible with NpmCommandLineRunner.
          diff --git a/vendor/closure-compiler/compiler.jar b/vendor/closure-compiler/compiler.jar new file mode 100644 index 000000000000..a12f24be30b3 Binary files /dev/null and b/vendor/closure-compiler/compiler.jar differ diff --git a/vendor/ng-closure-runner/LICENSE b/vendor/ng-closure-runner/LICENSE new file mode 100644 index 000000000000..6319d0484f51 --- /dev/null +++ b/vendor/ng-closure-runner/LICENSE @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2013-2020 Google LLC. http://angularjs.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. + diff --git a/vendor/ng-closure-runner/README.md b/vendor/ng-closure-runner/README.md new file mode 100644 index 000000000000..5174db08a6ec --- /dev/null +++ b/vendor/ng-closure-runner/README.md @@ -0,0 +1,34 @@ +# ng-closure-runner [![Build Status](https://travis-ci.org/angular/ng-closure-runner.png)](https://travis-ci.org/angular/ng-closure-runner) + +Wraps Google Closure Compiler for AngularJS-specific compile passes + +## Hacking + +ng-closure-runner is a lightweight runner around the +[Google Closure Compiler](https://developers.google.com/closure/compiler/). For +a complete description of how Closure Compiler works, refer to the +[source code](https://code.google.com/p/closure-compiler/source/browse/) and +[javadoc](http://javadoc.closure-compiler.googlecode.com/git/index.html). Refer +to `src/org/angularjs/closurerunner/MinerrPass.java` as an example of how to +write a custom compiler pass. + +We use [Gradle](http://www.gradle.org) to build. You'll need a current JDK +(version 1.6 or higher). To compile and run the tests: + +``` +$ gradle check +``` + +Submissions should include corresponding tests. + +## Releases + +Releases should be handled by the core Angular team. + +To create a new release: + +1. Run `gradle distZip`. +2. Commit the updated file in `assets/ng-closure-runner.zip` +3. Create a tag pointing to the commit. +4. In Angular, update the reference in `bower.json` to use the new tag. +5. That's it! You're done. diff --git a/vendor/ng-closure-runner/ngcompiler.jar b/vendor/ng-closure-runner/ngcompiler.jar new file mode 100644 index 000000000000..0dfc889202f1 Binary files /dev/null and b/vendor/ng-closure-runner/ngcompiler.jar differ diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000000..152936046125 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,11613 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@apidevtools/json-schema-ref-parser@^9.0.3": + version "9.0.7" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.7.tgz#64aa7f5b34e43d74ea9e408b90ddfba02050dde3" + integrity sha512-QdwOGF1+eeyFh+17v2Tz626WX0nucd1iKOm6JUTUvCZdbolblCOOQCxGrQPY0f7jEhn36PiAWqZnsC2r5vmUWg== + dependencies: + "@jsdevtools/ono" "^7.1.3" + call-me-maybe "^1.0.1" + js-yaml "^3.13.1" + +"@babel/code-frame@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== + +"@babel/highlight@^7.12.13": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@commitlint/execute-rule@^12.1.4": + version "12.1.4" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-12.1.4.tgz#9973b02e9779adbf1522ae9ac207a4815ec73de1" + integrity sha512-h2S1j8SXyNeABb27q2Ok2vD1WfxJiXvOttKuRA9Or7LN6OQoC/KtT3844CIhhWNteNMu/wE0gkTqGxDVAnJiHg== + +"@commitlint/load@>6.1.1": + version "12.1.4" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-12.1.4.tgz#e3c2dbc0e7d8d928f57a6878bd7219909fc0acab" + integrity sha512-Keszi0IOjRzKfxT+qES/n+KZyLrxy79RQz8wWgssCboYjKEp+wC+fLCgbiMCYjI5k31CIzIOq/16J7Ycr0C0EA== + dependencies: + "@commitlint/execute-rule" "^12.1.4" + "@commitlint/resolve-extends" "^12.1.4" + "@commitlint/types" "^12.1.4" + chalk "^4.0.0" + cosmiconfig "^7.0.0" + lodash "^4.17.19" + resolve-from "^5.0.0" + +"@commitlint/resolve-extends@^12.1.4": + version "12.1.4" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-12.1.4.tgz#e758ed7dcdf942618b9f603a7c28a640f6a0802a" + integrity sha512-R9CoUtsXLd6KSCfsZly04grsH6JVnWFmVtWgWs1KdDpdV+G3TSs37tColMFqglpkx3dsWu8dsPD56+D9YnJfqg== + dependencies: + import-fresh "^3.0.0" + lodash "^4.17.19" + resolve-from "^5.0.0" + resolve-global "^1.0.0" + +"@commitlint/types@^12.1.4": + version "12.1.4" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-12.1.4.tgz#9618a5dc8991fb58e6de6ed89d7bf712fa74ba7e" + integrity sha512-KRIjdnWNUx6ywz+SJvjmNCbQKcKP6KArhjZhY2l+CWKxak0d77SOjggkMwFTiSgLODOwmuLTbarR2ZfWPiPMlw== + dependencies: + chalk "^4.0.0" + +"@google-cloud/paginator@^3.0.0": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.5.tgz#9d6b96c421a89bd560c1bc2c197c7611ef21db6c" + integrity sha512-N4Uk4BT1YuskfRhKXBs0n9Lg2YTROZc6IMpkO/8DIHODtm5s3xY8K5vVBo23v/2XulY3azwITQlYWgT4GdLsUw== + dependencies: + arrify "^2.0.0" + extend "^3.0.2" + +"@google-cloud/precise-date@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@google-cloud/precise-date/-/precise-date-2.0.3.tgz#14f6f28ce35dabf3882e7aeab1c9d51bd473faed" + integrity sha512-+SDJ3ZvGkF7hzo6BGa8ZqeK3F6Z4+S+KviC9oOK+XCs3tfMyJCh/4j93XIWINgMMDIh9BgEvlw4306VxlXIlYA== + +"@google-cloud/projectify@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@google-cloud/projectify/-/projectify-2.0.1.tgz#13350ee609346435c795bbfe133a08dfeab78d65" + integrity sha512-ZDG38U/Yy6Zr21LaR3BTiiLtpJl6RkPS/JwoRT453G+6Q1DhlV0waNf8Lfu+YVYGIIxgKnLayJRfYlFJfiI8iQ== + +"@google-cloud/promisify@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.3.tgz#f934b5cdc939e3c7039ff62b9caaf59a9d89e3a8" + integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw== + +"@google-cloud/pubsub@^2.7.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@google-cloud/pubsub/-/pubsub-2.8.0.tgz#143f6c5f7dda1d22bf1ffd98104caf8c2611fca6" + integrity sha512-AoSKAbpHCoLq6jO9vMX+K6hJhkayafan24Rs2RKHU8Y0qF6IGSm1+ly0OG12TgziHWg818/6dljWWKgwDcp8KA== + dependencies: + "@google-cloud/paginator" "^3.0.0" + "@google-cloud/precise-date" "^2.0.0" + "@google-cloud/projectify" "^2.0.0" + "@google-cloud/promisify" "^2.0.0" + "@opentelemetry/api" "^0.12.0" + "@opentelemetry/tracing" "^0.12.0" + "@types/duplexify" "^3.6.0" + "@types/long" "^4.0.0" + arrify "^2.0.0" + extend "^3.0.2" + google-auth-library "^6.1.2" + google-gax "^2.9.2" + is-stream-ended "^0.1.4" + lodash.snakecase "^4.1.1" + p-defer "^3.0.0" + +"@grpc/grpc-js@~1.2.0": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.6.tgz#579c433ab9b9dda8a62080db1ac0c86dce58bbaa" + integrity sha512-wfYwFy7CvVEmBKzeDX1kQQYrv5NBpe8Z+VwXipFvqof3lCXKch7k+4T3grKtptaH5GQ5KP9iKwPr9hMDSynIUw== + dependencies: + "@types/node" ">=12.12.47" + google-auth-library "^6.1.1" + semver "^6.2.0" + +"@grpc/proto-loader@^0.5.1": + version "0.5.4" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.4.tgz#038a3820540f621eeb1b05d81fbedfb045e14de0" + integrity sha512-HTM4QpI9B2XFkPz7pjwMyMgZchJ93TVkL3kWPW8GDMDKYxsMnmf4w2TNMJK7+KNiYHS5cJrCEAFlF+AwtXWVPA== + dependencies: + lodash.camelcase "^4.3.0" + protobufjs "^6.8.6" + +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@opentelemetry/api@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-0.12.0.tgz#0359c3926e8f16fdcd8c78f196bd1e9fc4e66777" + integrity sha512-Dn4vU5GlaBrIWzLpsM6xbJwKHdlpwBQ4Bd+cL9ofJP3hKT8jBXpBpribmyaqAzrajzzl2Yt8uTa9rFVLfjDAvw== + dependencies: + "@opentelemetry/context-base" "^0.12.0" + +"@opentelemetry/context-base@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.12.0.tgz#4906ae27359d3311e3dea1b63770a16f60848550" + integrity sha512-UXwSsXo3F3yZ1dIBOG9ID8v2r9e+bqLWoizCtTb8rXtwF+N5TM7hzzvQz72o3nBU+zrI/D5e+OqAYK8ZgDd3DA== + +"@opentelemetry/core@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-0.12.0.tgz#a888badc9a408fa1f13976a574e69d14be32488e" + integrity sha512-oLZIkmTNWTJXzo1eA4dGu/S7wOVtylsgnEsCmhSJGhrJVDXm1eW/aGuNs3DVBeuxp0ZvQLAul3/PThsC3YrnzA== + dependencies: + "@opentelemetry/api" "^0.12.0" + "@opentelemetry/context-base" "^0.12.0" + semver "^7.1.3" + +"@opentelemetry/resources@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-0.12.0.tgz#5eb287c3032a2bebb2bb9f69b44bd160d2a7d591" + integrity sha512-8cYvIKB68cyupc7D6SWzkLtt13mbjgxMahL4JKCM6hWPyiGSJlPFEAey4XFXI5LLpPZRYTPHLVoLqI/xwCFZZA== + dependencies: + "@opentelemetry/api" "^0.12.0" + "@opentelemetry/core" "^0.12.0" + +"@opentelemetry/semantic-conventions@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-0.12.0.tgz#7e392aecdbdbd5d737d3995998b120dc17589ab0" + integrity sha512-BuCcDW0uLNYYTns0/LwXkJ8lp8aDm7kpS+WunEmPAPRSCe6ciOYRvzn5reqJfX93rf+6A3U2SgrBnCTH+0qoQQ== + +"@opentelemetry/tracing@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/tracing/-/tracing-0.12.0.tgz#769927721d417bfac85eef50c2af068bedce8873" + integrity sha512-2TUGhTGkhgnxTciHCNAILPSeyXageJewRqfP9wOrx65sKd/jgvNYoY8nYf4EVWVMirDOxKDsmYgUkjdQrwb2dg== + dependencies: + "@opentelemetry/api" "^0.12.0" + "@opentelemetry/context-base" "^0.12.0" + "@opentelemetry/core" "^0.12.0" + "@opentelemetry/resources" "^0.12.0" + "@opentelemetry/semantic-conventions" "^0.12.0" + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/duplexify@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@types/duplexify/-/duplexify-3.6.0.tgz#dfc82b64bd3a2168f5bd26444af165bf0237dcd8" + integrity sha512-5zOA53RUlzN74bvrSGwjudssD9F3a797sDZQkiYpUOxW+WHaXTCPz4/d5Dgi6FKnOqZ2CpaTo0DhgIfsXAOE/A== + dependencies: + "@types/node" "*" + +"@types/long@^4.0.0", "@types/long@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + +"@types/node@*": + version "14.0.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.4.tgz#43a63fc5edce226bed106b31b875165256271107" + integrity sha512-k3NqigXWRzQZVBDS5D1U70A5E8Qk4Kh+Ha/x4M8Bt9pF0X05eggfnC9+63Usc9Q928hRUIpIhTQaXsZwZBl4Ew== + +"@types/node@>=12.12.47": + version "14.14.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93" + integrity sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ== + +"@types/node@^13.7.0": + version "13.13.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.8.tgz#39fa1c8563bce1077507fea05699437f112ecbcc" + integrity sha512-WJoiKALUF5exZo0G3T5coauJR2Tmc6rdE9/kgppZVnV6rlUB2dl3gTu2GTNBKhKF6SZ/WFfpEUIGNC/0qvdMWA== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/q@^0.0.32": + version "0.0.32" + resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" + integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU= + +"@types/selenium-webdriver@^3.0.0": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.12.tgz#6affe5aed1ba379175075a889adbe2bc3aa62159" + integrity sha512-hYn+eoOehVUIdMwp5h34ZsGAO1ydja10GDup4BwyoFCdcH5MQ35nQq+AInSaBMEMopD5hEooFCyKo2Pajbe1ag== + +Base64@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" + integrity sha1-ujpCMHCOGGcFBl5mur3Uw1z2ACg= + +CSSselect@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/CSSselect/-/CSSselect-0.4.1.tgz#f8ab7e1f8418ce63cda6eb7bd778a85d7ec492b2" + integrity sha1-+Kt+H4QYzmPNput713ioXX7EkrI= + dependencies: + CSSwhat "0.4" + domutils "1.4" + +CSSwhat@0.4: + version "0.4.7" + resolved "https://registry.yarnpkg.com/CSSwhat/-/CSSwhat-0.4.7.tgz#867da0ff39f778613242c44cfea83f0aa4ebdf9b" + integrity sha1-hn2g/zn3eGEyQsRM/qg/CqTr35s= + +JSONStream@^1.0.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.0.tgz#680ab9ac6572a8a1a207e0b38721db1c77b215e5" + integrity sha1-aAq5rGVyqKGiB+CzhyHbHHeyFeU= + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +JSONStream@^1.2.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +JSONStream@~0.8.3, JSONStream@~0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-0.8.4.tgz#91657dfe6ff857483066132b4618b62e8f4887bd" + integrity sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70= + dependencies: + jsonparse "0.0.5" + through ">=2.2.7 <3" + +a-sync-waterfall@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/a-sync-waterfall/-/a-sync-waterfall-1.0.0.tgz#38e8319d79379e24628845b53b96722b29e0e47c" + integrity sha1-OOgxnXk3niRiiEW1O5ZyKyng5Hw= + +abbrev@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +accepts@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + integrity sha1-w8p0NJOGSMPg2cHjKN1otiLChMo= + dependencies: + mime-types "~2.1.11" + negotiator "0.6.1" + +accepts@~1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + +accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= + dependencies: + acorn "^3.0.4" + +acorn@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" + integrity sha1-F6jWp6bE71OLgU7Jq6wneSk78wo= + +acorn@4.X: + version "4.0.11" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.11.tgz#edcda3bd937e7556410d42ed5860f67399c794c0" + integrity sha1-7c2jvZN+dVZBDULtWGD2c5nHlMA= + +acorn@^1.0.3: + version "1.2.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014" + integrity sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ= + +acorn@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" + integrity sha1-q259nYhqrKiwhbwzEreaGYQz8Oc= + +acorn@^3.0.4, acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= + +adm-zip@^0.4.9: + version "0.4.14" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.14.tgz#2cf312bcc9f8875df835b0f6040bd89be0a727a9" + integrity sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g== + +adm-zip@~0.4.3: + version "0.4.11" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.11.tgz#2aa54c84c4b01a9d0fb89bb11982a51f13e3d62a" + integrity sha512-L8vcjDTCOIJk7wFvmlEUN7AsSb8T+2JrdP7KINBjzr24TJ5Mwj590sLu3BC7zNZowvJWa/JtPmD8eJCzdtDWjA== + +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + +agent-base@6, agent-base@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +ajv-keywords@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= + +ajv@^4.7.0: + version "4.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.2.tgz#f166c3c11cbc6cb9dcc102a5bcfe5b72c95287e6" + integrity sha1-8WbDwRy8bLncwQKlvP5bcslSh+Y= + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^6.12.2: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.5.5: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +angular-benchpress@0.x.x: + version "0.2.2" + resolved "https://registry.yarnpkg.com/angular-benchpress/-/angular-benchpress-0.2.2.tgz#05754d36e6248e061dbaf6a30a801c06217f1f60" + integrity sha1-BXVNNuYkjgYduvajCoAcBiF/H2A= + dependencies: + bootstrap "^3.2.0" + browserify "~7.0.0" + di "~2.0.0-pre-9" + express "^4.8.6" + minimist "^1.1.0" + mkdirp "^0.5.0" + rimraf "^2.2.8" + rx "~2.3.20" + underscore "^1.6.0" + +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== + dependencies: + string-width "^3.0.0" + +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= + +ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-regex@^0.2.0, ansi-regex@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" + integrity sha1-DY6UaWej2BQ/k+JOKYUl/BsiNfk= + +ansi-regex@^2.0.0, ansi-regex@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" + integrity sha1-6uy/Zs1waIJ2Cy9GkVgrj1XXp94= + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +archiver-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-1.3.0.tgz#e50b4c09c70bf3d680e32ff1b7994e9f9d895174" + integrity sha1-5QtMCccL89aA4y/xt5lOn52JUXQ= + dependencies: + glob "^7.0.0" + graceful-fs "^4.1.0" + lazystream "^1.0.0" + lodash "^4.8.0" + normalize-path "^2.0.0" + readable-stream "^2.0.0" + +archiver-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2" + integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw== + dependencies: + glob "^7.1.4" + graceful-fs "^4.2.0" + lazystream "^1.0.0" + lodash.defaults "^4.2.0" + lodash.difference "^4.5.0" + lodash.flatten "^4.4.0" + lodash.isplainobject "^4.0.6" + lodash.union "^4.6.0" + normalize-path "^3.0.0" + readable-stream "^2.0.0" + +archiver@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-1.3.0.tgz#4f2194d6d8f99df3f531e6881f14f15d55faaf22" + integrity sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI= + dependencies: + archiver-utils "^1.3.0" + async "^2.0.0" + buffer-crc32 "^0.2.1" + glob "^7.0.0" + lodash "^4.8.0" + readable-stream "^2.0.0" + tar-stream "^1.5.0" + walkdir "^0.0.11" + zip-stream "^1.1.0" + +archiver@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-3.1.1.tgz#9db7819d4daf60aec10fe86b16cb9258ced66ea0" + integrity sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg== + dependencies: + archiver-utils "^2.1.0" + async "^2.6.3" + buffer-crc32 "^0.2.1" + glob "^7.1.4" + readable-stream "^3.4.0" + tar-stream "^2.1.0" + zip-stream "^2.1.2" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + integrity sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0= + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= + +array-each@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" + integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + +array-flatten@1.1.1, array-flatten@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-3.0.0.tgz#6428ca2ee52c7b823192ec600fa3ed2f157cd541" + integrity sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA== + +array-slice@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" + integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1, array-uniq@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= + +arrify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +as-array@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/as-array/-/as-array-1.0.0.tgz#28a6eeeaa5729f1f4eca2047df5e9de1abda0ed1" + integrity sha1-KKbu6qVynx9OyiBH316d4avaDtE= + dependencies: + lodash.isarguments "2.4.x" + lodash.isobject "^2.4.1" + lodash.values "^2.4.1" + +as-array@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/as-array/-/as-array-2.0.0.tgz#4f04805d87f8fce8e511bc2108f8e5e3a287d547" + integrity sha1-TwSAXYf4/OjlEbwhCPjl46KH1Uc= + +asap@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" + integrity sha1-UidltQw1EEkOUtfc/ghe+bqWlY8= + +asn1.js@^4.0.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + integrity sha1-SLokC0WpKA6UdImQull9IWYX/UA= + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.1.2.tgz#adaa04c46bb58c6dd1f294da3eb26e6228eb6e44" + integrity sha1-raoExGu1jG3R8pTaPrJuYijrbkQ= + dependencies: + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-types@^0.13.2: + version "0.13.4" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" + integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== + dependencies: + tslib "^2.0.1" + +astw@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astw/-/astw-2.0.0.tgz#08121ac8288d35611c0ceec663f6cd545604897d" + integrity sha1-CBIayCiNNWEcDO7GY/bNVFYEiX0= + dependencies: + acorn "^1.0.3" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + integrity sha1-GdOGodntxufByF04iu28xW0zYC0= + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== + +async@1.5.2, async@^1.3.0, async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.0, async@^2.1.2, async@^2.6.1, async@^2.6.2, async@^2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= + +async@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" + integrity sha1-+PwEyjoTeErenhZBr5hXjPvWR6k= + +async@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + integrity sha1-ri1acpR38onWDdf5amMUoi3Wwio= + +atob@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" + integrity sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" + integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== + +babel-code-frame@^6.16.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + integrity sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ= + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + +balanced-match@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + +base64-js@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.7.tgz#54400dc91d696cec32a8a47902f971522fee8f48" + integrity sha1-VEANyR1pbOwyqKR5AvlxUi/uj0g= + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== + +base64-js@^1.2.3, base64-js@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + +base64id@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" + integrity sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY= + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +basic-auth-connect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz#fdb0b43962ca7b40456a7c2bb48fe173da2d2122" + integrity sha1-/bC0OWLKe0BFanwrtI/hc9otISI= + +basic-auth@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-1.1.0.tgz#45221ee429f7ee1e5035be3f51533f1cdfd29884" + integrity sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ= + +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +batch@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" + integrity sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +beeper@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= + +benchmark@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-1.0.0.tgz#2f1e2fa4c359f11122aa183082218e957e390c73" + integrity sha1-Lx4vpMNZ8REiqhgwgiGOlX45DHM= + +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + +big-integer@^1.6.17: + version "1.6.48" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" + integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + +bignumber.js@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + integrity sha1-RqoXUftqL5PuXmibsQh9SxTGwgU= + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + +binary@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" + integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk= + dependencies: + buffers "~0.1.1" + chainsaw "~0.1.0" + +bl@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.3.tgz#1e8dd80142eac80d7158c9dccc047fb620e035e7" + integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +bl@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" + integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A== + dependencies: + readable-stream "^3.0.1" + +bl@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a" + integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +blob@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" + integrity sha1-vPEwUspURj8w+fx+lbmkdjCpSSE= + +blocking-proxy@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/blocking-proxy/-/blocking-proxy-1.0.1.tgz#81d6fd1fe13a4c0d6957df7f91b75e98dac40cb2" + integrity sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA== + dependencies: + minimist "^1.2.0" + +bluebird@^3.3.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== + +bluebird@^3.4.6, bluebird@~3.4.1: + version "3.4.7" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +body-parser@1.19.0, body-parser@^1.18.3, body-parser@^1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +body-parser@^1.16.1: + version "1.18.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + integrity sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.1" + http-errors "~1.6.2" + iconv-lite "0.4.19" + on-finished "~2.3.0" + qs "6.5.1" + raw-body "2.3.2" + type-is "~1.6.15" + +bootstrap@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.1.1.tgz#17e14ed261c0fcd9b52ea9aa6420f6d51cd5fa77" + integrity sha1-F+FO0mHA/Nm1LqmqZCD21RzV+nc= + +bootstrap@^3.2.0: + version "3.3.7" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71" + integrity sha1-WjiTlFSfIzMIdaOxUGVldPip63E= + +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + +brace-expansion@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + integrity sha1-cZfX6qm4fmSDkOph/GbIRCdCDfk= + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.0, braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browser-pack@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-3.2.0.tgz#faa1cbc41487b1acc4747e373e1148adffd0e2d9" + integrity sha1-+qHLxBSHsazEdH43PhFIrf/Q4tk= + dependencies: + JSONStream "~0.8.4" + combine-source-map "~0.3.0" + concat-stream "~1.4.1" + defined "~0.0.0" + through2 "~0.5.1" + umd "^2.1.0" + +browser-resolve@^1.3.0, browser-resolve@^1.7.0: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + integrity sha1-j/CbCixCFxihBRwmCzLkj0QpOM4= + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + integrity sha1-Xncl297x/Vkw1OurSFZ85FHEigo= + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + integrity sha1-mYgkSHS/XtTijalWZtzWasj8Njo= + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + integrity sha1-2qJ3cXRwki7S/hhZQRihdUOXId0= + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" + integrity sha1-EHc5EMPCBtVCCkaq2GlPgguFlo8= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0= + dependencies: + pako "~0.2.0" + +browserify@~7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-7.0.3.tgz#b839f84ed22c24b67f79af68002e5684c73d534e" + integrity sha1-uDn4TtIsJLZ/ea9oAC5WhMc9U04= + dependencies: + JSONStream "~0.8.3" + assert "~1.1.0" + browser-pack "^3.2.0" + browser-resolve "^1.3.0" + browserify-zlib "~0.1.2" + buffer "^2.3.0" + builtins "~0.0.3" + commondir "0.0.1" + concat-stream "~1.4.1" + console-browserify "^1.1.0" + constants-browserify "~0.0.1" + crypto-browserify "^3.0.0" + deep-equal "~0.2.1" + defined "~0.0.0" + deps-sort "^1.3.5" + domain-browser "~1.1.0" + duplexer2 "~0.0.2" + events "~1.0.0" + glob "^4.0.5" + http-browserify "^1.4.0" + https-browserify "~0.0.0" + inherits "~2.0.1" + insert-module-globals "^6.1.0" + isarray "0.0.1" + labeled-stream-splicer "^1.0.0" + module-deps "^3.6.3" + os-browserify "~0.1.1" + parents "~0.0.1" + path-browserify "~0.0.0" + process "^0.8.0" + punycode "~1.2.3" + querystring-es3 "~0.2.0" + readable-stream "^1.0.33-1" + resolve "~0.7.1" + shallow-copy "0.0.1" + shasum "^1.0.0" + shell-quote "~0.0.1" + stream-browserify "^1.0.0" + string_decoder "~0.10.0" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^1.0.0" + timers-browserify "^1.0.1" + tty-browserify "~0.0.0" + umd "~2.1.0" + url "~0.10.1" + util "~0.10.1" + vm-browserify "~0.0.1" + xtend "^3.0.0" + +browserstack-local@^1.3.7: + version "1.4.2" + resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.4.2.tgz#5d2248384b8aa0fc521df32001127f010a92458d" + integrity sha512-fRaynjF0MvtyyfPRy2NFnVwxLyNtD28K/v9xRsIjUVf7xLc80NIm7Nfr3KXlFmWizhG91PL/UAOXlHkoxQjaNw== + dependencies: + https-proxy-agent "^2.2.1" + is-running "^2.0.0" + ps-tree "=1.1.1" + temp-fs "^0.9.9" + +browserstack@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.5.1.tgz#e2dfa66ffee940ebad0a07f7e00fd4687c455d66" + integrity sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg== + dependencies: + https-proxy-agent "^2.2.1" + +browserstack@~1.5.1: + version "1.5.3" + resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.5.3.tgz#93ab48799a12ef99dbd074dd595410ddb196a7ac" + integrity sha512-AO+mECXsW4QcqC9bxwM29O7qWa7bJT94uBFzeb5brylIQwawuEziwq20dPYbins95GlWzOawgyDNdjYAo32EKg== + dependencies: + https-proxy-agent "^2.2.1" + +browserstacktunnel-wrapper@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/browserstacktunnel-wrapper/-/browserstacktunnel-wrapper-2.0.4.tgz#0ebffd3d6311b8526c30d8b430fdc651a535eebb" + integrity sha512-GCV599FUUxNOCFl3WgPnfc5dcqq9XTmMXoxWpqkvmk0R9TOIoqmjENNU6LY6DtgIL6WfBVbg/jmWtnM5K6UYSg== + dependencies: + https-proxy-agent "^2.2.1" + unzipper "^0.9.3" + +buffer-alloc-unsafe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" + integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== + +buffer-alloc@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" + integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== + dependencies: + buffer-alloc-unsafe "^1.1.0" + buffer-fill "^1.0.0" + +buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +buffer-fill@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" + integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= + +buffer-indexof-polyfill@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz#a9fb806ce8145d5428510ce72f278bb363a638bf" + integrity sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8= + +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^2.3.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-2.8.2.tgz#d73c214c0334384dc29b04ee0ff5f5527c7974e7" + integrity sha1-1zwhTAM0OE3CmwTuD/X1Unx5dOc= + dependencies: + base64-js "0.0.7" + ieee754 "^1.1.4" + is-array "^1.0.1" + +buffer@^5.1.0, buffer@^5.5.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + +buffers@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" + integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s= + +bufferstreams@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/bufferstreams/-/bufferstreams-1.1.1.tgz#0161373060ac5988eff99058731114f6e195d51e" + integrity sha1-AWE3MGCsWYjv+ZBYcxEU9uGV1R4= + dependencies: + readable-stream "^2.0.2" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + +builtins@~0.0.3: + version "0.0.7" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-0.0.7.tgz#355219cd6cf18dbe7c01cc7fd2dce765cfdc549a" + integrity sha1-NVIZzWzxjb58Acx/0tznZc/cVJo= + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +cachedir@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.2.0.tgz#19afa4305e05d79e417566882e0c8f960f62ff0e" + integrity sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ== + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= + dependencies: + callsites "^0.2.0" + +callsite@1.0.0, callsite@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= + +camelcase@^2.0.0, camelcase@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +canonical-path@0.0.2, canonical-path@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-0.0.2.tgz#e31eb937a8c93ee2a01df1839794721902874574" + integrity sha1-4x65N6jJPuKgHfGDl5RyGQKHRXQ= + +cardinal@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" + integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= + dependencies: + ansicolors "~0.3.2" + redeyed "~2.1.0" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +catharsis@^0.8.1: + version "0.8.8" + resolved "https://registry.yarnpkg.com/catharsis/-/catharsis-0.8.8.tgz#693479f43aac549d806bd73e924cd0d944951a06" + integrity sha1-aTR59DqsVJ2Aa9c+kkzQ2USVGgY= + dependencies: + underscore-contrib "~0.3.0" + +chainsaw@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" + integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg= + dependencies: + traverse ">=0.3.0 <0.4" + +chalk@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.0.tgz#375dfccbc21c0a60a8b61bc5b78f3dc2a55c212f" + integrity sha1-N138y8IcCmCothvFt489wqVcIS8= + dependencies: + ansi-styles "^1.1.0" + escape-string-regexp "^1.0.0" + has-ansi "^0.1.0" + strip-ansi "^0.3.0" + supports-color "^0.2.0" + +chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +change-case@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.0.tgz#6c9c8e35f8790870a82b6b0745be8c3cbef9b081" + integrity sha1-bJyONfh5CHCoK2sHRb6MPL75sIE= + dependencies: + camel-case "^3.0.0" + constant-case "^2.0.0" + dot-case "^2.1.0" + header-case "^1.0.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + no-case "^2.2.0" + param-case "^2.1.0" + pascal-case "^2.0.0" + path-case "^2.1.0" + sentence-case "^2.1.0" + snake-case "^2.1.0" + swap-case "^1.1.0" + title-case "^2.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + +changez-angular@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/changez-angular/-/changez-angular-2.1.3.tgz#4bf25429baf121818008a1d7b720c72ea7d55c57" + integrity sha1-S/JUKbrxIYGACKHXtyDHLqfVXFc= + dependencies: + changez "^2.1.0" + nunjucks-date "^1.2.0" + +changez@^2.1.0, changez@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/changez/-/changez-2.1.2.tgz#f2ac8753ecccdbc7828bac9a336a7cc0ac72f1e8" + integrity sha512-EN/L0IOJxfwUawwrDVurzkK+HCfkG2J9GstLubAih8H7+p0TdqK6qF/U3FI4bh9UijJvpc7/16QMj4Dh7mBOUA== + dependencies: + commander "^2.19.0" + find-package "^1.0.0" + nunjucks "^3.2.0" + shelljs "^0.7.4" + simple-node-logger "^0.93.42" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +cheerio@^0.17.0: + version "0.17.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.17.0.tgz#fa5ae42cc60121133d296d0b46d983215f7268ea" + integrity sha1-+lrkLMYBIRM9KW0LRtmDIV9yaOo= + dependencies: + CSSselect "~0.4.0" + dom-serializer "~0.0.0" + entities "~1.1.1" + htmlparser2 "~3.7.2" + lodash "~2.4.1" + +chokidar@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.3.tgz#dcbd4f6cbb2a55b4799ba8a840ac527e5f4b1176" + integrity sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.0" + optionalDependencies: + fsevents "^1.1.2" + +chokidar@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +chokidar@^3.0.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" + integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.4.0" + optionalDependencies: + fsevents "~2.1.2" + +chownr@^1.0.1, chownr@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" + integrity sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" + integrity sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc= + dependencies: + inherits "^2.0.1" + +circular-json@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" + integrity sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0= + +cjson@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/cjson/-/cjson-0.3.3.tgz#a92d9c786e5bf9b930806329ee05d5d3261b4afa" + integrity sha1-qS2ceG5b+bkwgGMp7gXV0yYbSvo= + dependencies: + json-parse-helpfulerror "^1.0.3" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-color@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-1.4.0.tgz#7d10738f48526824f8fe7da51857cb0f572fe01f" + integrity sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w== + dependencies: + ansi-regex "^2.1.1" + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + memoizee "^0.4.14" + timers-ext "^0.1.5" + +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + integrity sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc= + dependencies: + restore-cursor "^1.0.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-spinners@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5" + integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w== + +cli-table@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" + integrity sha1-9TsFJmqLGguTSz0IIebi3FkUriM= + dependencies: + colors "1.0.3" + +cli-width@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + integrity sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao= + +cliui@^3.0.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +clone-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" + integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone-stats@^0.0.1, clone-stats@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= + +clone-stats@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" + integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= + +clone@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" + integrity sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8= + +clone@^1.0.0, clone@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + integrity sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk= + +cloneable-readable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117" + integrity sha1-pikNQT8hemEjL5XkWP84QYz7ARc= + dependencies: + inherits "^2.0.1" + process-nextick-args "^1.0.6" + through2 "^2.0.1" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +coffee-script@~1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.7.1.tgz#62996a861780c75e6d5069d13822723b73404bfc" + integrity sha1-YplqhheAx15tUGnROCJyO3NAS/w= + dependencies: + mkdirp "~0.3.5" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +collections@^0.2.0, collections@~0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/collections/-/collections-0.2.2.tgz#1f23026b2ef36f927eecc901e99c5f0d48fa334e" + integrity sha1-HyMCay7zb5J+7MkB6ZxfDUj6M04= + dependencies: + weak-map "1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" + integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w== + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colornames@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/colornames/-/colornames-1.1.1.tgz#f8889030685c7c4ff9e2a559f5077eb76a816f96" + integrity sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y= + +colors@1.0.3, colors@1.0.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + +colors@^1.1.0, colors@^1.1.2, colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= + +colors@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +colorspace@1.1.x: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.2.tgz#e0128950d082b86a2168580796a0aa5d6c68d8c5" + integrity sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ== + dependencies: + color "3.0.x" + text-hex "1.0.x" + +combine-source-map@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.3.0.tgz#d9e74f593d9cd43807312cb5d846d451efaa9eb7" + integrity sha1-2edPWT2c1DgHMSy12EbUUe+qnrc= + dependencies: + convert-source-map "~0.3.0" + inline-source-map "~0.3.0" + source-map "~0.1.31" + +combine-source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.6.1.tgz#9b4a09c316033d768e0f11e029fa2730e079ad96" + integrity sha1-m0oJwxYDPXaODxHgKfonMOB5rZY= + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.5.0" + lodash.memoize "~3.0.3" + source-map "~0.4.2" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + integrity sha1-+mihT2qUXVTbvlDYzbMyDp47GgY= + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + integrity sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM= + +commander@>=1.1, commander@^2.19.0, commander@^2.9.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commitizen@^4.0.3, commitizen@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-4.2.4.tgz#a3e5b36bd7575f6bf6e7aa19dbbf06b0d8f37165" + integrity sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw== + dependencies: + cachedir "2.2.0" + cz-conventional-changelog "3.2.0" + dedent "0.7.0" + detect-indent "6.0.0" + find-node-modules "^2.1.2" + find-root "1.1.0" + fs-extra "8.1.0" + glob "7.1.4" + inquirer "6.5.2" + is-utf8 "^0.2.1" + lodash "^4.17.20" + minimist "1.2.5" + strip-bom "4.0.0" + strip-json-comments "3.0.1" + +commitplease@^2.7.10: + version "2.7.10" + resolved "https://registry.yarnpkg.com/commitplease/-/commitplease-2.7.10.tgz#129af5abb365b46f25e652020c5d1548c947f163" + integrity sha1-Epr1q7NltG8l5lICDF0VSMlH8WM= + dependencies: + chalk "^1.1.1" + git-tools "^0.2.1" + ini "^1.3.4" + object-assign "^4.1.0" + semver "^5.1.0" + +commondir@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-0.0.1.tgz#89f00fdcd51b519c578733fec563e6a6da7f5be2" + integrity sha1-ifAP3NUbUZxXhzP+xWPmptp/W+I= + +compare-semver@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/compare-semver/-/compare-semver-1.1.0.tgz#7c0a79a27bb80b6c6994445f82958259d3d02153" + integrity sha1-fAp5onu4C2xplERfgpWCWdPQIVM= + dependencies: + semver "^5.0.1" + +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1, component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + +compress-commons@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.2.tgz#524a9f10903f3a813389b0225d27c48bb751890f" + integrity sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8= + dependencies: + buffer-crc32 "^0.2.1" + crc32-stream "^2.0.0" + normalize-path "^2.0.0" + readable-stream "^2.0.0" + +compress-commons@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-2.1.1.tgz#9410d9a534cf8435e3fbbb7c6ce48de2dc2f0610" + integrity sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q== + dependencies: + buffer-crc32 "^0.2.13" + crc32-stream "^3.0.1" + normalize-path "^3.0.0" + readable-stream "^2.3.6" + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.0: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +concat-stream@^1.4.6, concat-stream@~1.4.1, concat-stream@~1.4.5: + version "1.4.10" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" + integrity sha1-rMO79WAsuMyYDGrIQPp9hgPj7zY= + dependencies: + inherits "~2.0.1" + readable-stream "~1.1.9" + typedarray "~0.0.5" + +concat-with-sourcemaps@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.0.4.tgz#f55b3be2aeb47601b10a2d5259ccfb70fd2f1dd6" + integrity sha1-9Vs74q60dgGxCi1SWcz7cP0vHdY= + dependencies: + source-map "^0.5.1" + +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +connect-livereload@^0.5.0: + version "0.5.4" + resolved "https://registry.yarnpkg.com/connect-livereload/-/connect-livereload-0.5.4.tgz#80157d1371c9f37cc14039ab1895970d119dc3bc" + integrity sha1-gBV9E3HJ83zBQDmrGJWXDRGdw7w= + +connect@^3.4.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.5.0.tgz#b357525a0b4c1f50599cd983e1d9efeea9677198" + integrity sha1-s1dSWgtMH1BZnNmD4dnv7qlncZg= + dependencies: + debug "~2.2.0" + finalhandler "0.5.0" + parseurl "~1.3.1" + utils-merge "1.0.0" + +connect@^3.6.0: + version "3.6.5" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.5.tgz#fb8dde7ba0763877d0ec9df9dac0b4b40e72c7da" + integrity sha1-+43ee6B2OHfQ7J352sC0tA5yx9o= + dependencies: + debug "2.6.9" + finalhandler "1.0.6" + parseurl "~1.3.2" + utils-merge "1.0.1" + +connect@^3.6.2: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +constant-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" + integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= + dependencies: + snake-case "^2.1.0" + upper-case "^1.1.1" + +constants-browserify@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-0.0.1.tgz#92577db527ba6c4cf0a4568d84bc031f441e21f2" + integrity sha1-kld9tSe6bEzwpFaNhLwDH0QeIfI= + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-type@^1.0.4, content-type@~1.0.2, content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +conventional-commit-types@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz#7c9214e58eae93e85dd66dbfbafe7e4fffa2365b" + integrity sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg== + +convert-source-map@1.X: + version "1.3.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" + integrity sha1-6fPpxuJyjvwmdmlqcOs4L3MQamc= + +convert-source-map@~0.3.0: + version "0.3.5" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= + +convert-source-map@~1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" + integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA= + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" + integrity sha1-+rg/uwstjchfpjbEudNMdUIMbWU= + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +crc32-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4" + integrity sha1-483TtN8xaN10494/u8t7KX/pCPQ= + dependencies: + crc "^3.4.4" + readable-stream "^2.0.0" + +crc32-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-3.0.1.tgz#cae6eeed003b0e44d739d279de5ae63b171b4e85" + integrity sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w== + dependencies: + crc "^3.4.4" + readable-stream "^3.4.0" + +crc@^3.4.4: + version "3.8.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" + integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== + dependencies: + buffer "^5.1.0" + +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + integrity sha1-iIxyNZbN92EvZJgjPuvXo1MBc30= + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" + integrity sha1-USEAYte7dHn2xlu0GpIgix1hq60= + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^1.0.0" + sha.js "^2.3.6" + +create-hmac@^1.1.0, create-hmac@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" + integrity sha1-0/tLolPriz9W456i+8uK90e9MXA= + dependencies: + create-hash "^1.1.0" + inherits "^2.0.1" + +cross-env@^5.1.3: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" + integrity sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ== + dependencies: + cross-spawn "^6.0.5" + +cross-spawn@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" + integrity sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE= + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.0.1.tgz#a3bbb302db2297cbea3c04edf36941f4613aa399" + integrity sha1-o7uzAtsil8vqPATt82lB9GE6o5k= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.1: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@^3.0.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" + integrity sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI= + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css@2.X: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css/-/css-2.2.1.tgz#73a4c81de85db664d4ee674f7d47085e3b2d55dc" + integrity sha1-c6TIHehdtmTU7mdPfUcIXjstVdw= + dependencies: + inherits "^2.0.1" + source-map "^0.1.38" + source-map-resolve "^0.3.0" + urix "^0.1.0" + +csv-streamify@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/csv-streamify/-/csv-streamify-3.0.4.tgz#4cb614c57e3f299cca17b63fdcb4ad167777f47a" + integrity sha1-TLYUxX4/KZzKF7Y/3LStFnd39Ho= + dependencies: + through2 "2.0.1" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU= + +cycle@1.0.x: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" + integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= + +cz-conventional-changelog@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-1.1.4.tgz#c0e643d419113601c4ebd9b173db9d751fcfdd41" + integrity sha1-wOZD1BkRNgHE69mxc9uddR/P3UE= + dependencies: + word-wrap "^1.0.3" + +cz-conventional-changelog@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-3.2.0.tgz#6aef1f892d64113343d7e455529089ac9f20e477" + integrity sha512-yAYxeGpVi27hqIilG1nh4A9Bnx4J3Ov+eXy4koL3drrR+IO9GaWPsKjik20ht608Asqi8TQPf0mczhEeyAtMzg== + dependencies: + chalk "^2.4.1" + commitizen "^4.0.3" + conventional-commit-types "^3.0.0" + lodash.map "^4.5.1" + longest "^2.0.1" + word-wrap "^1.0.3" + optionalDependencies: + "@commitlint/load" ">6.1.1" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +d@^0.1.1, d@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + integrity sha1-2hhMU10Y2O57oqoim5FACfrhEwk= + dependencies: + es5-ext "~0.10.2" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-uri-to-buffer@3: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" + integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== + +date-format@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" + integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= + +dateformat@^1.0.7-1.2.3: + version "1.0.12" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= + dependencies: + get-stdin "^4.0.1" + meow "^3.3.0" + +dateformat@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.0.0.tgz#2743e3abb5c3fc2462e527dca445e04e9f4dee17" + integrity sha1-J0Pjq7XD/CRi5SfcpEXgTp9N7hc= + +dateformat@~3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" + integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== + +debug-fabulous@0.0.X: + version "0.0.4" + resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-0.0.4.tgz#fa071c5d87484685424807421ca4b16b0b1a0763" + integrity sha1-+gccXYdIRoVCSAdCHKSxawsaB2M= + dependencies: + debug "2.X" + lazy-debug-legacy "0.0.X" + object-assign "4.1.0" + +debug@2.2.0, debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= + dependencies: + ms "0.7.1" + +debug@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" + integrity sha1-vFlryr52F/Edn6FTYe3tVgi4SZs= + dependencies: + ms "0.7.2" + +debug@2.6.9, debug@2.X, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +debug@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" + integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== + dependencies: + ms "^2.1.1" + +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + +dedent@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + +deep-equal@~0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-0.2.2.tgz#84b745896f34c684e98f2ce0e42abaf43bba017d" + integrity sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0= + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-freeze@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" + integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ= + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defaults@^1.0.0, defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +defined@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-0.0.0.tgz#f35eea7d705e933baf13b2f03b3f83d921403b3e" + integrity sha1-817qfXBekzuvE7LwOz+D2SFAOz4= + +degenerator@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-2.2.0.tgz#49e98c11fa0293c5b26edfbb52f15729afcdb254" + integrity sha512-aiQcQowF01RxFI4ZLFMpzyotbQonhNpBao6dkI8JPk5a+hmSjR5ErHp2CQySmQe8os3VBqLCIh87nDBgZXvsmg== + dependencies: + ast-types "^0.13.2" + escodegen "^1.8.1" + esprima "^4.0.0" + +del@^2.0.2, del@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + integrity sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag= + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@1.1.1, depd@~1.1.0, depd@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +dependency-graph@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.4.1.tgz#302e58218d85c51a97638730dbf9b7d852a19693" + integrity sha1-MC5YIY2FxRqXY4cw2/m32FKhlpM= + +deprecated@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/deprecated/-/deprecated-0.0.1.tgz#f9c9af5464afa1e7a971458a8bdef2aa94d5bb19" + integrity sha1-+cmvVGSvoeepcUWKi97yqpTVuxk= + +deps-sort@^1.3.5: + version "1.3.9" + resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-1.3.9.tgz#29dfff53e17b36aecae7530adbbbf622c2ed1a71" + integrity sha1-Kd//U+F7Nq7K51MK27v2IsLtGnE= + dependencies: + JSONStream "^1.0.3" + shasum "^1.0.0" + subarg "^1.0.0" + through2 "^1.0.0" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@^1.0.4, destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-file@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" + integrity sha1-STXe39lIhkjgBrASlWbpOGcR6mM= + dependencies: + fs-exists-sync "^0.1.0" + +detect-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" + integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= + +detect-indent@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" + integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== + +detect-libc@^1.0.2, detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= + +detect-newline@2.X: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= + +detective@^4.0.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" + integrity sha1-d2l+LnlHrD/nyOJqbW8RUjWvqRw= + dependencies: + acorn "^3.1.0" + defined "^1.0.0" + +dgeni-packages@^0.26.5: + version "0.26.5" + resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.26.5.tgz#a76da27b40ce92dfc37a9e629ef9f1d3897f6bde" + integrity sha512-szGvJaanZDqDUhhZXXlFjidjUOX/fsKACjbtQHLxiwg7Fr5qugXX0fp7Zg8ZyJ0v8d9ix0N3KKQBM7Jo58YbiQ== + dependencies: + canonical-path "0.0.2" + catharsis "^0.8.1" + change-case "3.0.0" + dgeni "^0.4.9" + espree "^2.2.3" + estraverse "^4.1.0" + glob "^7.0.5" + htmlparser2 "^3.7.3" + lodash "^4.13.1" + marked "^0.3.2" + minimatch "^3.0.2" + mkdirp "^0.5.1" + mkdirp-promise "^5.0.0" + node-html-encoder "0.0.2" + nunjucks "^3.0.1" + semver "^5.2.0" + shelljs "^0.7.0" + source-map-support "^0.4.15" + spdx-license-list "^2.1.0" + stringmap "^0.2.2" + typescript "~2.7.1" + urlencode "^1.1.0" + +dgeni@^0.4.9: + version "0.4.9" + resolved "https://registry.yarnpkg.com/dgeni/-/dgeni-0.4.9.tgz#9e42775b1386ca5eb824753ac2cd169d8f61ced1" + integrity sha1-nkJ3WxOGyl64JHU6ws0WnY9hztE= + dependencies: + canonical-path "~0.0.2" + dependency-graph "~0.4.1" + di "0.0.1" + lodash "^3.10.1" + objectdiff "^1.1.0" + optimist "~0.6.1" + q "~1.4.1" + validate.js "^0.9.0" + winston "^2.1.1" + +di@0.0.1, di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= + +di@~2.0.0-pre-9: + version "2.0.0-pre-9" + resolved "https://registry.yarnpkg.com/di/-/di-2.0.0-pre-9.tgz#b51fb4c3a7a1cb231396e1abce4f0ecfe187b6df" + integrity sha1-tR+0w6ehyyMTluGrzk8Oz+GHtt8= + dependencies: + es6-shim "~0.9.2" + traceur vojtajina/traceur-compiler#add-es6-pure-transformer-dist + +diagnostics@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" + integrity sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ== + dependencies: + colorspace "1.1.x" + enabled "1.0.x" + kuler "1.0.x" + +diff@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + integrity sha1-fyjS657nsVqX79ic5j3P2qPMur8= + +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + integrity sha1-tYNXOScM/ias9jIJn97SoH8gnl4= + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +doctrine@^1.2.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +dom-serialize@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs= + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +dom-serializer@0, dom-serializer@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.0.1.tgz#9589827f1e32d22c37c829adabd59b3247af8eaf" + integrity sha1-lYmCfx4y0iw3yCmtq9WbMkevjq8= + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domain-browser@~1.1.0: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + integrity sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw= + +domelementtype@1, domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= + +domhandler@2.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.2.1.tgz#59df9dcd227e808b365ae73e1f6684ac3d946fc2" + integrity sha1-Wd+dzSJ+gIs2Wuc+H2aErD2Ub8I= + dependencies: + domelementtype "1" + +domutils@1.4: + version "1.4.3" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" + integrity sha1-CGVRN5bGswYDGFDhdVFrr4C3Km8= + dependencies: + domelementtype "1" + +domutils@1.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.0.tgz#4b43dd0d7403c34cb645424add397e80bfe85ca6" + integrity sha1-S0PdDXQDw0y2RUJK3Tl+gL/oXKY= + dependencies: + no-case "^2.2.0" + +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== + dependencies: + is-obj "^2.0.0" + +dotenv@^6.1.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" + integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== + +duplexer2@0.0.2, duplexer2@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= + dependencies: + readable-stream "~1.1.9" + +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= + dependencies: + readable-stream "^2.0.2" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= + +duplexify@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" + integrity sha512-DY3xVEmVHTv1wSzKNbwoU6nVjzI369Y6sPoqfYr0/xlx3IdX2n94xIszTcjPO8W8ZIv0Wb0PXNcjuZyT4wiICA== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +edge-launcher@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/edge-launcher/-/edge-launcher-1.2.2.tgz#eb40aafbd067a6ea76efffab0647bcd5509b37b2" + integrity sha1-60Cq+9Bnpup27/+rBke81VCbN7I= + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +elliptic@^6.0.0: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +enabled@1.0.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" + integrity sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M= + dependencies: + env-variable "0.0.x" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + integrity sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +end-of-stream@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-0.1.5.tgz#8e177206c3c80837d85632e8b9359dfe8b2f6eaf" + integrity sha1-jhdyBsPICDfYVjLouTWd/osvbq8= + dependencies: + once "~1.3.0" + +engine.io-client@~3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.2.1.tgz#6f54c0475de487158a1a7c77d10178708b6add36" + integrity sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw== + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.1.1" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~3.3.1" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196" + integrity sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.5" + blob "0.0.4" + has-binary2 "~1.0.2" + +engine.io@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.2.0.tgz#54332506f42f2edc71690d2f2a42349359f3bf7d" + integrity sha512-mRbgmAtQ4GAlKwuPnnAvXXwdPhEx+jkc0OBCLrXuD/CRvwNK3AxRSnqK4FSqmAMRRHryVJP8TopOvmEaA64fKw== + dependencies: + accepts "~1.3.4" + base64id "1.0.0" + cookie "0.3.1" + debug "~3.1.0" + engine.io-parser "~2.1.0" + ws "~3.3.1" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= + +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + integrity sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY= + +entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= + +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== + +env-variable@0.0.x: + version "0.0.6" + resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.6.tgz#74ab20b3786c545b62b4a4813ab8cf22726c9808" + integrity sha512-bHz59NlBbtS0NhftmR8+ExBEekE7br0e01jw+kk0NDro7TtZzBYZ5ScGPs3OmwnpyfHTHOtr1Y6uedCdrIldtg== + +error-ex@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" + integrity sha1-5ntD8+gsluo6WE/+4Ln8MyXYAtk= + dependencies: + is-arrayish "^0.2.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14, es5-ext@~0.10.46: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: + version "0.10.12" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" + integrity sha1-qoRkHU23a2Krul5F/YBey6sUAEc= + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.0.tgz#bd968567d61635e33c0b80727613c9cb4b096bac" + integrity sha1-vZaFZ9YWNeM8C4BydhPJy0sJa6w= + dependencies: + d "^0.1.1" + es5-ext "^0.10.7" + es6-symbol "3" + +es6-iterator@^2.0.3, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-map@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.4.tgz#a34b147be224773a4d7da8072794cefa3632b897" + integrity sha1-o0sUe+IkdzpNfagHJ5TO+jYyuJc= + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-iterator "2" + es6-set "~0.1.3" + es6-symbol "~3.1.0" + event-emitter "~0.3.4" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promise@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" + integrity sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y= + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +es6-set@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8" + integrity sha1-lRa2dhwpZLkv9HlFYjOiR9xwfOg= + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-iterator "2" + es6-symbol "3" + event-emitter "~0.3.4" + +es6-shim@~0.9.2: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.9.3.tgz#00c725e75b0ae4d322e6ccbd87484a800237f03a" + integrity sha1-AMcl51sK5NMi5sy9h0hKgAI38Do= + +es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" + integrity sha1-lEgcZV56fK2C66gy2X1UM0ltf/o= + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.1.tgz#0d2bbd8827eb5fb4ba8f97fbfea50d43db21ea81" + integrity sha1-DSu9iCfrX7S6j5f7/qUNQ9sh6oE= + dependencies: + d "^0.1.1" + es5-ext "^0.10.8" + es6-iterator "2" + es6-symbol "3" + +es6-weak-map@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + integrity sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE= + +escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escodegen@^1.8.1: + version "1.14.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" + integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== + dependencies: + esprima "^4.0.1" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-plugin-promise@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.6.0.tgz#54b7658c8f454813dc2a870aff8152ec4969ba75" + integrity sha512-YQzM6TLTlApAr7Li8vWKR+K3WghjwKcYzY0d2roWap4SLK+kzuagJX/leTetIDWsFcTFnKNJXWupDCD6aZkP2Q== + +eslint@^3.0.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.15.0.tgz#bdcc6a6c5ffe08160e7b93c066695362a91e30f2" + integrity sha1-vcxqbF/+CBYOe5PAZmlTYqkeMPI= + dependencies: + babel-code-frame "^6.16.0" + chalk "^1.1.3" + concat-stream "^1.4.6" + debug "^2.1.1" + doctrine "^1.2.2" + escope "^3.6.0" + espree "^3.4.0" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + glob "^7.0.3" + globals "^9.14.0" + ignore "^3.2.0" + imurmurhash "^0.1.4" + inquirer "^0.12.0" + is-my-json-valid "^2.10.0" + is-resolvable "^1.0.0" + js-yaml "^3.5.1" + json-stable-stringify "^1.0.0" + levn "^0.3.0" + lodash "^4.0.0" + mkdirp "^0.5.0" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.1" + pluralize "^1.2.1" + progress "^1.1.8" + require-uncached "^1.0.2" + shelljs "^0.7.5" + strip-bom "^3.0.0" + strip-json-comments "~2.0.1" + table "^3.7.8" + text-table "~0.2.0" + user-home "^2.0.0" + +espree@^2.2.3: + version "2.2.5" + resolved "https://registry.yarnpkg.com/espree/-/espree-2.2.5.tgz#df691b9310889402aeb29cc066708c56690b854b" + integrity sha1-32kbkxCIlAKuspzAZnCMVmkLhUs= + +espree@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.0.tgz#41656fa5628e042878025ef467e78f125cb86e1d" + integrity sha1-QWVvpWKOBCh4Al70Z+ePEly4bh0= + dependencies: + acorn "4.0.4" + acorn-jsx "^3.0.0" + +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esrecurse@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + integrity sha1-RxO2U2rffyrE8yfVWed1a/9kgiA= + dependencies: + estraverse "~4.1.0" + object-assign "^4.0.1" + +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= + +estraverse@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" + integrity sha1-9srKcokzqFDvkGYdDheYK6RxEaI= + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= + +etag@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" + integrity sha1-A9MLX2fdbmMtKUXTDWZScxo01dg= + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +event-emitter@~0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.4.tgz#8d63ddfb4cfe1fae3b32ca265c4c720222080bb5" + integrity sha1-jWPd+0z+H647MsomXExyAiIIC7U= + dependencies: + d "~0.1.1" + es5-ext "~0.10.7" + +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +event-stream@~3.1.0: + version "3.1.7" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.1.7.tgz#b4c540012d0fe1498420f3d8946008db6393c37a" + integrity sha1-tMVAAS0P4UmEIPPYlGAI22OTw3o= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.2" + stream-combiner "~0.0.4" + through "~2.3.1" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter2@~0.4.13: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + integrity sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas= + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events-listener@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/events-listener/-/events-listener-1.1.0.tgz#dd49b4628480eba58fde31b870ee346b3990b349" + integrity sha512-Kd3EgYfODHueq6GzVfs/VUolh2EgJsS8hkO3KpnDrxVjU3eq63eXM2ujXkhPP+OkeUOhL8CxdfZbQXzryb5C4g== + +events@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/events/-/events-1.0.2.tgz#75849dcfe93d10fb057c30055afdbd51d06a8e24" + integrity sha1-dYSdz+k9EPsFfDAFWv29UdBqjiQ= + +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + integrity sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM= + dependencies: + create-hash "^1.1.1" + +exegesis-express@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/exegesis-express/-/exegesis-express-2.0.0.tgz#e33b2ed35e52162ce78613868a771ee4cb5636a7" + integrity sha512-NKvKBsBa2OvU+1BFpWbz3PzoRMhA9q7/wU2oMmQ9X8lPy/FRatADvhlkGO1zYOMgeo35k1ZLO9ZV0uIs9pPnXg== + dependencies: + exegesis "^2.0.0" + +exegesis@^2.0.0: + version "2.5.6" + resolved "https://registry.yarnpkg.com/exegesis/-/exegesis-2.5.6.tgz#2a5f198a857b6d820f6bfa0ad41fe29e6fe97446" + integrity sha512-e+YkH/zZTN2njiwrV8tY6tHGDsFu3LyR/YbrqdWvDZaAJ5YGWaBYyd3oX/Y26iGqQc+7jLEKLDTv2UPzjAYL8w== + dependencies: + "@apidevtools/json-schema-ref-parser" "^9.0.3" + ajv "^6.12.2" + body-parser "^1.18.3" + content-type "^1.0.4" + deep-freeze "0.0.1" + events-listener "^1.1.0" + glob "^7.1.3" + json-ptr "^1.3.1" + json-schema-traverse "^0.4.1" + lodash "^4.17.11" + openapi3-ts "^1.2.0" + promise-breaker "^5.0.0" + pump "^3.0.0" + qs "^6.6.0" + raw-body "^2.3.3" + semver "^7.0.0" + +exit-code@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/exit-code/-/exit-code-1.0.2.tgz#ce165811c9f117af6a5f882940b96ae7f9aecc34" + integrity sha1-zhZYEcnxF69qX4gpQLlq5/muzDQ= + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + integrity sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g= + +exit@^0.1.2, exit@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= + dependencies: + fill-range "^2.1.0" + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +expand-tilde@^1.2.1, expand-tilde@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" + integrity sha1-C4HrqJflo9MdHD0QL48BRB5VlEk= + dependencies: + os-homedir "^1.0.1" + +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= + dependencies: + homedir-polyfill "^1.0.1" + +express@^4.16.4: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +express@^4.8.6: + version "4.14.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.14.1.tgz#646c237f766f148c2120aff073817b9e4d7e0d33" + integrity sha1-ZGwjf3ZvFIwhIK/wc4F7nk1+DTM= + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.2" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "~2.2.0" + depd "~1.1.0" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + finalhandler "0.5.1" + fresh "0.3.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.3" + qs "6.2.0" + range-parser "~1.2.0" + send "0.14.2" + serve-static "~1.11.2" + type-is "~1.6.14" + utils-merge "1.0.0" + vary "~1.1.0" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +eyes@0.1.x: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= + +fancy-log@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" + integrity sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg= + dependencies: + chalk "^1.1.1" + time-stamp "^1.0.0" + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.4, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fast-text-encoding@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.2.tgz#ff1ad5677bde049e0f8656aa6083a7ef2c5836e2" + integrity sha512-5rQdinSsycpzvAoHga2EDn+LRX1d5xLFsuNG0Kg61JrAT/tASXcLL0nf/33v+sAxlQcfYmWbTURa1mmAf55jGw== + +fast-text-encoding@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" + integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== + +fast-url-parser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= + dependencies: + punycode "^1.3.2" + +fecha@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" + integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +file-sync-cmp@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz#a5e7a8ffbfa493b43b923bbd4ca89a53b63b612b" + integrity sha1-peeo/7+kk7Q7kju9TKiaU7Y7YSs= + +file-uri-to-path@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" + integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= + +filesize@^3.1.3: + version "3.6.1" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" + integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + integrity sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM= + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" + integrity sha1-6VCKvs6bbbqHGmlCodeRG5GRGsc= + dependencies: + debug "~2.2.0" + escape-html "~1.0.3" + on-finished "~2.3.0" + statuses "~1.3.0" + unpipe "~1.0.0" + +finalhandler@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.1.tgz#2c400d8d4530935bc232549c5fa385ec07de6fcd" + integrity sha1-LEANjUUwk1vCMlScX6OF7Afeb80= + dependencies: + debug "~2.2.0" + escape-html "~1.0.3" + on-finished "~2.3.0" + statuses "~1.3.1" + unpipe "~1.0.0" + +finalhandler@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.6.tgz#007aea33d1a4d3e42017f624848ad58d212f814f" + integrity sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8= + dependencies: + debug "2.6.9" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.3.1" + unpipe "~1.0.0" + +finalhandler@1.1.2, finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-index@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" + integrity sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ= + +find-node-modules@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-2.1.2.tgz#57565a3455baf671b835bc6b2134a9b938b9c53c" + integrity sha512-x+3P4mbtRPlSiVE1Qco0Z4YLU8WFiFcuWTf3m75OV9Uzcfs2Bg+O9N+r/K0AnmINBW06KpfqKwYJbFlFq4qNug== + dependencies: + findup-sync "^4.0.0" + merge "^2.1.0" + +find-package@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-package/-/find-package-1.0.0.tgz#d7738da67e3c5f055c24d3e19aa1aeed063c3e83" + integrity sha1-13ONpn48XwVcJNPhmqGu7QY8PoM= + dependencies: + parents "^1.0.1" + +find-root@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +findup-sync@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.2.tgz#a8117d0f73124f5a4546839579fe52d7129fb5e5" + integrity sha1-qBF9D3MST1pFRoOVef5S1xKfteU= + dependencies: + detect-file "^0.1.0" + is-glob "^2.0.1" + micromatch "^2.3.7" + resolve-dir "^0.1.0" + +findup-sync@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-4.0.0.tgz#956c9cdde804052b881b428512905c4a5f2cdef0" + integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== + dependencies: + detect-file "^1.0.0" + is-glob "^4.0.0" + micromatch "^4.0.2" + resolve-dir "^1.0.1" + +findup-sync@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.3.0.tgz#37930aa5d816b777c03445e1966cc6790a4c0b16" + integrity sha1-N5MKpdgWt3fANEXhlmzGeQpMCxY= + dependencies: + glob "~5.0.0" + +fined@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.0.2.tgz#5b28424b760d7598960b7ef8480dff8ad3660e97" + integrity sha1-WyhCS3YNdZiWC374SA3/itNmDpc= + dependencies: + expand-tilde "^1.2.1" + lodash.assignwith "^4.0.7" + lodash.isempty "^4.2.1" + lodash.isplainobject "^4.0.4" + lodash.isstring "^4.0.1" + lodash.pick "^4.2.1" + parse-filepath "^1.0.1" + +fined@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" + integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== + dependencies: + expand-tilde "^2.0.2" + is-plain-object "^2.0.3" + object.defaults "^1.1.0" + object.pick "^1.2.0" + parse-filepath "^1.0.1" + +firebase-tools@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/firebase-tools/-/firebase-tools-9.3.0.tgz#8bb8dec8218a0cd8db8694ebd5f5a6c9bf495ce7" + integrity sha512-tLg1GKpf8Jxlgqq3JttWTOHsOKIyNpro0Ic5sOabmxd1XSeXdK0XW3r3INASoSpy1SYPQn5EEyR1Vs/a0gCu2w== + dependencies: + "@google-cloud/pubsub" "^2.7.0" + JSONStream "^1.2.1" + abort-controller "^3.0.0" + archiver "^3.0.0" + body-parser "^1.19.0" + chokidar "^3.0.2" + cjson "^0.3.1" + cli-color "^1.2.0" + cli-table "^0.3.1" + commander "^4.0.1" + configstore "^5.0.1" + cross-env "^5.1.3" + cross-spawn "^7.0.1" + csv-streamify "^3.0.4" + dotenv "^6.1.0" + exegesis-express "^2.0.0" + exit-code "^1.0.2" + express "^4.16.4" + filesize "^3.1.3" + fs-extra "^0.23.1" + glob "^7.1.2" + google-auth-library "^6.1.3" + inquirer "~6.3.1" + js-yaml "^3.13.1" + jsonschema "^1.0.2" + jsonwebtoken "^8.2.1" + leven "^3.1.0" + lodash "^4.17.19" + marked "^0.7.0" + marked-terminal "^3.3.0" + minimatch "^3.0.4" + morgan "^1.10.0" + node-fetch "^2.6.1" + open "^6.3.0" + ora "^3.4.0" + plist "^3.0.1" + portfinder "^1.0.23" + progress "^2.0.3" + proxy-agent "^4.0.0" + request "^2.87.0" + rimraf "^3.0.0" + semver "^5.7.1" + superstatic "^7.1.0" + tar "^4.3.0" + tcp-port-used "^1.0.1" + tmp "0.0.33" + triple-beam "^1.3.0" + tweetsodium "0.0.5" + universal-analytics "^0.4.16" + unzipper "^0.10.10" + update-notifier "^4.1.0" + uuid "^3.0.0" + winston "^3.0.0" + ws "^7.2.3" + +first-chunk-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" + integrity sha1-Wb+1DNkF9g18OUzT2ayqtOatk04= + +flagged-respawn@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" + integrity sha1-/xke3c1wiKZ1smEP/8l2vpuAdLU= + +flagged-respawn@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" + integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== + +flat-arguments@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flat-arguments/-/flat-arguments-1.0.2.tgz#9baa780adf0501f282d726c9c6a038dba44ea76f" + integrity sha1-m6p4Ct8FAfKC1ybJxqA426ROp28= + dependencies: + array-flatten "^1.0.0" + as-array "^1.0.0" + lodash.isarguments "^3.0.0" + lodash.isobject "^3.0.0" + +flat-cache@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" + integrity sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y= + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +flatted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" + integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== + +follow-redirects@^1.0.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + +for-own@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +formatio@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" + integrity sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek= + dependencies: + samsam "~1.1" + +forwarded@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" + integrity sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M= + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" + integrity sha1-ZR+DjiJCTnVm3hYdg1jKoZn4PU8= + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from@~0: + version "0.1.3" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.3.tgz#ef63ac2062ac32acf7862e0d40b44b896f22f3bc" + integrity sha1-72OsIGKsMqz3hi4NQLRLiW8i87w= + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= + +fs-extra@8.1.0, fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.23.1.tgz#6611dba6adf2ab8dc9c69fab37cddf8818157e3d" + integrity sha1-ZhHbpq3yq43Jxp+rN83fiBgVfj0= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + dependencies: + minipass "^2.2.1" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.1.2: + version "1.2.9" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.9.tgz#3f5ed66583ccd6f400b5a00db6f7e861363e388f" + integrity sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw== + dependencies: + nan "^2.12.1" + node-pre-gyp "^0.12.0" + +fsevents@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + +fsevents@~2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" + integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== + +fstream@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" + integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +ftp@^0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaxios@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.1.0.tgz#e8ad466db5a4383c70b9d63bfd14dfaa87eb0099" + integrity sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg== + dependencies: + abort-controller "^3.0.0" + extend "^3.0.2" + https-proxy-agent "^5.0.0" + is-stream "^2.0.0" + node-fetch "^2.3.0" + +gaze@^0.5.1, gaze@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.5.2.tgz#40b709537d24d1d45767db5a908689dfe69ac44f" + integrity sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8= + dependencies: + globule "~0.1.0" + +gcp-metadata@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62" + integrity sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw== + dependencies: + gaxios "^4.0.0" + json-bigint "^1.0.0" + +generate-function@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= + dependencies: + is-property "^1.0.0" + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-uri@3: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" + integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== + dependencies: + "@tootallnate/once" "1" + data-uri-to-buffer "3" + debug "4" + file-uri-to-path "2" + fs-extra "^8.1.0" + ftp "^0.3.10" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getobject@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/getobject/-/getobject-1.0.1.tgz#17d86a05913c15d173a5bcf8662dc7c7ac5ce147" + integrity sha512-tj18lLe+917AACr6BdVoUuHnBPTVd9BEJp1vxnMZ58ztNvuxz9Ufa+wf3g37tlGITH35jggwZ2d9lcgHJJgXfQ== + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +git-tools@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/git-tools/-/git-tools-0.2.1.tgz#6e1846af2c0e91ab59258b48f9b53c1279b3b273" + integrity sha1-bhhGrywOkatZJYtI+bU8EnmzsnM= + dependencies: + spawnback "~1.0.0" + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= + dependencies: + is-glob "^2.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== + dependencies: + is-glob "^4.0.1" + +glob-slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/glob-slash/-/glob-slash-1.0.0.tgz#fe52efa433233f74a2fe64c7abb9bc848202ab95" + integrity sha1-/lLvpDMjP3Si/mTHq7m8hIICq5U= + +glob-slasher@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/glob-slasher/-/glob-slasher-1.0.1.tgz#747a0e5bb222642ee10d3e05443e109493cb0f8e" + integrity sha1-dHoOW7IiZC7hDT4FRD4QlJPLD44= + dependencies: + glob-slash "^1.0.0" + lodash.isobject "^2.4.1" + toxic "^1.0.0" + +glob-stream@^3.1.5: + version "3.1.18" + resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-3.1.18.tgz#9170a5f12b790306fdfe598f313f8f7954fd143b" + integrity sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs= + dependencies: + glob "^4.3.1" + glob2base "^0.0.12" + minimatch "^2.0.1" + ordered-read-streams "^0.1.0" + through2 "^0.6.1" + unique-stream "^1.0.0" + +glob-watcher@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-0.0.6.tgz#b95b4a8df74b39c83298b0c05c978b4d9a3b710b" + integrity sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs= + dependencies: + gaze "^0.5.1" + +glob2base@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" + integrity sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY= + dependencies: + find-index "^0.1.1" + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + +glob@7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^4.0.5: + version "4.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-4.0.6.tgz#695c50bdd4e2fb5c5d370b091f388d3707e291a7" + integrity sha1-aVxQvdTi+1xdNwsJHziNNwfikac= + dependencies: + graceful-fs "^3.0.2" + inherits "2" + minimatch "^1.0.0" + once "^1.3.0" + +glob@^4.3.1: + version "4.5.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" + integrity sha1-xstz0yJsHv7wTePFbQEvAzd+4V8= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "^2.0.1" + once "^1.3.0" + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.5: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.3, glob@^7.0.6: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + integrity sha1-gFIR3wT6rxxjo2ADBs31reULLsg= + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~3.1.21: + version "3.1.21" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" + integrity sha1-0p4KBV3qUTj00H7UDomC6DwgZs0= + dependencies: + graceful-fs "~1.2.0" + inherits "1" + minimatch "~0.2.11" + +glob@~5.0.0: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~7.1.6: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= + dependencies: + ini "^1.3.4" + +global-dirs@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" + integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== + dependencies: + ini "1.3.7" + +global-modules@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" + integrity sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0= + dependencies: + global-prefix "^0.1.4" + is-windows "^0.2.0" + +global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" + integrity sha1-jTvGuNo8qBEqFg2NSW/wRiv+948= + dependencies: + homedir-polyfill "^1.0.0" + ini "^1.3.4" + is-windows "^0.2.0" + which "^1.2.12" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + +globals@^9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.14.0.tgz#8859936af0038741263053b39d0e76ca241e4034" + integrity sha1-iFmTavADh0EmMFOznQ52yiQeQDQ= + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + integrity sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0= + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globule@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-0.1.0.tgz#d9c8edde1da79d125a151b79533b978676346ae5" + integrity sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU= + dependencies: + glob "~3.1.21" + lodash "~1.0.1" + minimatch "~0.2.11" + +glogg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" + integrity sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U= + dependencies: + sparkles "^1.0.0" + +google-auth-library@^6.1.1, google-auth-library@^6.1.2, google-auth-library@^6.1.3: + version "6.1.6" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" + integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^4.0.0" + gcp-metadata "^4.2.0" + gtoken "^5.0.4" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-code-prettify@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/google-code-prettify/-/google-code-prettify-1.0.1.tgz#72ed730414769db2ecc470f4ec413174ccb76327" + integrity sha1-cu1zBBR2nbLsxHD07EExdMy3Yyc= + +google-gax@^2.9.2: + version "2.10.2" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.10.2.tgz#6d3dc047140e57987d0a2c2d9791faac9bc959b1" + integrity sha512-adECud3d5jsk24SvPkKQG3Kw1szpy4We0OqKfsdBHKWlSWhdY4hVQEOG7iBBp469Zm327fzz7NZz8BMLOYZJHg== + dependencies: + "@grpc/grpc-js" "~1.2.0" + "@grpc/proto-loader" "^0.5.1" + "@types/long" "^4.0.0" + abort-controller "^3.0.0" + duplexify "^4.0.0" + fast-text-encoding "^1.0.3" + google-auth-library "^6.1.3" + is-stream-ended "^0.1.4" + node-fetch "^2.6.1" + protobufjs "^6.10.2" + retry-request "^4.0.0" + +google-p12-pem@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.3.tgz#673ac3a75d3903a87f05878f3c75e06fc151669e" + integrity sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA== + dependencies: + node-forge "^0.10.0" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@4.X, graceful-fs@^3.0.0, graceful-fs@^3.0.2, graceful-fs@^4.1.0, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@~1.2.0: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + integrity sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8= + +growl@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.7.0.tgz#de2d66136d002e112ba70f3f10c31cf7c350b2da" + integrity sha1-3i1mE20ALhErpw8/EMMc98NQsto= + +grunt-bump@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/grunt-bump/-/grunt-bump-0.8.0.tgz#d3ffe0cf3cf0b38e09607b78538f42a531eafe55" + integrity sha1-0//gzzzws44JYHt4U49CpTHq/lU= + dependencies: + semver "^5.1.0" + +grunt-cli@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/grunt-cli/-/grunt-cli-1.2.0.tgz#562b119ebb069ddb464ace2845501be97b35b6a8" + integrity sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg= + dependencies: + findup-sync "~0.3.0" + grunt-known-options "~1.1.0" + nopt "~3.0.6" + resolve "~1.1.0" + +grunt-cli@~1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/grunt-cli/-/grunt-cli-1.4.3.tgz#22c9f1a3d2780bf9b0d206e832e40f8f499175ff" + integrity sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ== + dependencies: + grunt-known-options "~2.0.0" + interpret "~1.1.0" + liftup "~3.0.1" + nopt "~4.0.1" + v8flags "~3.2.0" + +grunt-contrib-clean@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-clean/-/grunt-contrib-clean-1.0.0.tgz#6b2ed94117e2c7ffe32ee04578c96fe4625a9b6d" + integrity sha1-ay7ZQRfix//jLuBFeMlv5GJam20= + dependencies: + async "^1.5.2" + rimraf "^2.5.1" + +grunt-contrib-compress@^1.3.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-compress/-/grunt-contrib-compress-1.6.0.tgz#9708885c738a97a12c5f3072dc97dbc31b4121db" + integrity sha512-wIFuvk+/Ny4E+OgEfJYFZgoH7KcU/nnNFbYasB7gRvrcRyW6vmTp3Pj8a4rFSR3tbFMjrGvTUszdO6fgLajgZQ== + dependencies: + archiver "^1.3.0" + chalk "^1.1.1" + lodash "^4.7.0" + pretty-bytes "^4.0.2" + stream-buffers "^2.1.0" + optionalDependencies: + iltorb "^2.4.3" + +grunt-contrib-connect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/grunt-contrib-connect/-/grunt-contrib-connect-1.0.2.tgz#5cf933b91a67386044273c0b2444603cd98879ba" + integrity sha1-XPkzuRpnOGBEJzwLJERgPNmIebo= + dependencies: + async "^1.5.2" + connect "^3.4.0" + connect-livereload "^0.5.0" + http2 "^3.3.4" + morgan "^1.6.1" + opn "^4.0.0" + portscanner "^1.0.0" + serve-index "^1.7.1" + serve-static "^1.10.0" + +grunt-contrib-copy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz#7060c6581e904b8ab0d00f076e0a8f6e3e7c3573" + integrity sha1-cGDGWB6QS4qw0A8HbgqPbj58NXM= + dependencies: + chalk "^1.1.1" + file-sync-cmp "^0.1.0" + +grunt-ddescribe-iit@~0.0.1: + version "0.0.7" + resolved "https://registry.yarnpkg.com/grunt-ddescribe-iit/-/grunt-ddescribe-iit-0.0.7.tgz#932ff7e475a0e0c9526d6c5ed71d1ad4e76c2203" + integrity sha1-ky/35HWg4MlSbWxe1x0a1OdsIgM= + dependencies: + bluebird "^3.4.6" + cross-spawn "^5.0.1" + +grunt-eslint@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/grunt-eslint/-/grunt-eslint-19.0.0.tgz#bb74c379061599cec1f66169def2a89d862d861b" + integrity sha1-u3TDeQYVmc7B9mFp3vKonYYthhs= + dependencies: + chalk "^1.0.0" + eslint "^3.0.0" + +grunt-known-options@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-1.1.0.tgz#a4274eeb32fa765da5a7a3b1712617ce3b144149" + integrity sha1-pCdO6zL6dl2lp6OxcSYXzjsUQUk= + +grunt-known-options@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/grunt-known-options/-/grunt-known-options-2.0.0.tgz#cac641e897f9a0a680b8c9839803d35f3325103c" + integrity sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA== + +grunt-legacy-log-utils@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz#49a8c7dc74051476dcc116c32faf9db8646856ef" + integrity sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw== + dependencies: + chalk "~4.1.0" + lodash "~4.17.19" + +grunt-legacy-log@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz#1c6eaf92371ea415af31ea84ce50d434ef6d39c4" + integrity sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA== + dependencies: + colors "~1.1.2" + grunt-legacy-log-utils "~2.1.0" + hooker "~0.2.3" + lodash "~4.17.19" + +grunt-legacy-util@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz#0f929d13a2faf9988c9917c82bff609e2d9ba255" + integrity sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w== + dependencies: + async "~3.2.0" + exit "~0.1.2" + getobject "~1.0.0" + hooker "~0.2.3" + lodash "~4.17.21" + underscore.string "~3.3.5" + which "~2.0.2" + +grunt-merge-conflict@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/grunt-merge-conflict/-/grunt-merge-conflict-0.0.2.tgz#7b4f83c810865ece5dca0f3cd474d0208a7d7acc" + integrity sha1-e0+DyBCGXs5dyg881HTQIIp9esw= + +grunt-shell@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/grunt-shell/-/grunt-shell-1.3.1.tgz#5e2beecd05d5d3787fa401028d5733d5d43b9bd1" + integrity sha1-XivuzQXV03h/pAECjVcz1dQ7m9E= + dependencies: + chalk "^1.0.0" + npm-run-path "^1.0.0" + object-assign "^4.0.0" + +grunt@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/grunt/-/grunt-1.4.1.tgz#7d1e17db1f9c8108777f7273d6b9359755576f50" + integrity sha512-ZXIYXTsAVrA7sM+jZxjQdrBOAg7DyMUplOMhTaspMRExei+fD0BTwdWXnn0W5SXqhb/Q/nlkzXclSi3IH55PIA== + dependencies: + dateformat "~3.0.3" + eventemitter2 "~0.4.13" + exit "~0.1.2" + findup-sync "~0.3.0" + glob "~7.1.6" + grunt-cli "~1.4.2" + grunt-known-options "~2.0.0" + grunt-legacy-log "~3.0.0" + grunt-legacy-util "~2.0.1" + iconv-lite "~0.4.13" + js-yaml "~3.14.0" + minimatch "~3.0.4" + mkdirp "~1.0.4" + nopt "~3.0.6" + rimraf "~3.0.2" + +gtoken@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16" + integrity sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw== + dependencies: + gaxios "^4.0.0" + google-p12-pem "^3.0.3" + jws "^4.0.0" + +gulp-concat@^2.4.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" + integrity sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M= + dependencies: + concat-with-sourcemaps "^1.0.0" + through2 "^2.0.0" + vinyl "^2.0.0" + +gulp-eslint@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/gulp-eslint/-/gulp-eslint-3.0.1.tgz#04e57e3e18c6974267c12cf6855dc717d4a313bd" + integrity sha1-BOV+PhjGl0JnwSz2hV3HF9SjE70= + dependencies: + bufferstreams "^1.1.1" + eslint "^3.0.0" + gulp-util "^3.0.6" + +gulp-foreach@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/gulp-foreach/-/gulp-foreach-0.0.1.tgz#d3ff554fcdcdbbebdc4aa4fc5a41441d3c6f7567" + integrity sha1-0/9VT83Nu+vcSqT8WkFEHTxvdWc= + dependencies: + gulp-util "~2.2.14" + through "~2.3.4" + +gulp-rename@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.2.2.tgz#3ad4428763f05e2764dec1c67d868db275687817" + integrity sha1-OtRCh2PwXidk3sHGfYaNsnVoeBc= + +gulp-sourcemaps@^1.2.2: + version "1.11.1" + resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.11.1.tgz#b534376deab49387d406c3aebeb8beaf02ef3548" + integrity sha1-tTQ3beq0k4fUBsOuvri+rwLvNUg= + dependencies: + acorn "4.X" + convert-source-map "1.X" + css "2.X" + debug-fabulous "0.0.X" + detect-newline "2.X" + graceful-fs "4.X" + source-map "0.X" + strip-bom "2.X" + through2 "2.X" + vinyl "1.X" + +gulp-uglify@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-3.0.2.tgz#5f5b2e8337f879ca9dec971feb1b82a5a87850b0" + integrity sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg== + dependencies: + array-each "^1.0.1" + extend-shallow "^3.0.2" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + isobject "^3.0.1" + make-error-cause "^1.1.1" + safe-buffer "^5.1.2" + through2 "^2.0.0" + uglify-js "^3.0.5" + vinyl-sourcemaps-apply "^0.2.0" + +gulp-util@^3.0.0, gulp-util@^3.0.1, gulp-util@^3.0.6: + version "3.0.8" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" + integrity sha1-AFTh50RQLifATBh8PsxQXdVLu08= + dependencies: + array-differ "^1.0.0" + array-uniq "^1.0.2" + beeper "^1.0.0" + chalk "^1.0.0" + dateformat "^2.0.0" + fancy-log "^1.1.0" + gulplog "^1.0.0" + has-gulplog "^0.1.0" + lodash._reescape "^3.0.0" + lodash._reevaluate "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.template "^3.0.0" + minimist "^1.1.0" + multipipe "^0.1.2" + object-assign "^3.0.0" + replace-ext "0.0.1" + through2 "^2.0.0" + vinyl "^0.5.0" + +gulp-util@~2.2.14: + version "2.2.20" + resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-2.2.20.tgz#d7146e5728910bd8f047a6b0b1e549bc22dbd64c" + integrity sha1-1xRuVyiRC9jwR6awseVJvCLb1kw= + dependencies: + chalk "^0.5.0" + dateformat "^1.0.7-1.2.3" + lodash._reinterpolate "^2.4.1" + lodash.template "^2.4.1" + minimist "^0.2.0" + multipipe "^0.1.0" + through2 "^0.5.0" + vinyl "^0.2.1" + +gulp@~3.8.0: + version "3.8.11" + resolved "https://registry.yarnpkg.com/gulp/-/gulp-3.8.11.tgz#d557e0a7283eb4136491969b0497767972f1d28a" + integrity sha1-1Vfgpyg+tBNkkZabBJd2eXLx0oo= + dependencies: + archy "^1.0.0" + chalk "^0.5.0" + deprecated "^0.0.1" + gulp-util "^3.0.0" + interpret "^0.3.2" + liftoff "^2.0.1" + minimist "^1.1.0" + orchestrator "^0.3.0" + pretty-hrtime "^0.2.0" + semver "^4.1.0" + tildify "^1.0.0" + v8flags "^2.0.2" + vinyl-fs "^0.3.0" + +gulplog@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= + dependencies: + glogg "^1.0.0" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-ansi@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" + integrity sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4= + dependencies: + ansi-regex "^0.2.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= + dependencies: + ansi-regex "^2.0.0" + +has-binary2@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98" + integrity sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg= + dependencies: + isarray "2.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-gulplog@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= + dependencies: + sparkles "^1.0.0" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +header-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.0.tgz#d9e335909505d56051ec16a0106821889e910781" + integrity sha1-2eM1kJUF1WBR7BagEGghiJ6RB4E= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.3" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/home-dir/-/home-dir-1.0.0.tgz#2917eb44bdc9072ceda942579543847e3017fe4e" + integrity sha1-KRfrRL3JByztqUJXlUOEfjAX/k4= + +homedir-polyfill@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + integrity sha1-TCu8inWJmP7r9e1oWA921GdotLw= + dependencies: + parse-passwd "^1.0.0" + +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hooker@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959" + integrity sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk= + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +htmlparser2@^3.7.3, htmlparser2@~3.7.2: + version "3.7.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.7.3.tgz#6a64c77637c08c6f30ec2a8157a53333be7cb05e" + integrity sha1-amTHdjfAjG8w7CqBV6UzM758sF4= + dependencies: + domelementtype "1" + domhandler "2.2" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + +http-browserify@^1.4.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/http-browserify/-/http-browserify-1.7.0.tgz#33795ade72df88acfbfd36773cefeda764735b20" + integrity sha1-M3la3nLfiKz7/TZ3PO/tp2RzWyA= + dependencies: + Base64 "~0.2.0" + inherits "~2.0.1" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.6.2, http-errors@~1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY= + dependencies: + depd "1.1.1" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.7.3, http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.5.0, http-errors@~1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" + integrity sha1-eIwNLB3iyBuebowBhDtrl+uSB1A= + dependencies: + inherits "2.0.3" + setprototypeof "1.0.2" + statuses ">= 1.3.1 < 2" + +http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-proxy@^1.13.0: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http2@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/http2/-/http2-3.3.6.tgz#7df06227e02b5b5a5841deea08239b3198d04bec" + integrity sha1-ffBiJ+ArW1pYQd7qCCObMZjQS+w= + +https-browserify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + integrity sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI= + +https-proxy-agent@5, https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +iconv-lite@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.4.4, iconv-lite@~0.4.11: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@~0.4.13: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + integrity sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es= + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + integrity sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q= + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +ignore@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410" + integrity sha1-HFHh71O6tt3BXbTZrE7BOezrNBA= + +iltorb@^2.4.3: + version "2.4.4" + resolved "https://registry.yarnpkg.com/iltorb/-/iltorb-2.4.4.tgz#7ec303bbbd8c0cd4d44a847eb6c6d8490f9c7433" + integrity sha512-7Qk6O7TK3rSWVRVRkPehcNTSN+P2i7MsG9pWmw6iVw/W6NcoNj0rFKOuBDM6fbZV6NNGuUW3JBRem6Ozn4KXhg== + dependencies: + detect-libc "^1.0.3" + nan "^2.14.0" + npmlog "^4.1.2" + prebuild-install "^5.3.2" + which-pm-runs "^1.0.0" + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" + integrity sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js= + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +ini@1.3.7, ini@^1.3.4, ini@~1.3.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" + integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== + +inline-source-map@~0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.3.1.tgz#a528b514e689fce90db3089e870d92f527acb5eb" + integrity sha1-pSi1FOaJ/OkNswiehw2S9Sestes= + dependencies: + source-map "~0.3.0" + +inline-source-map@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.5.0.tgz#4a4c5dd8e4fb5e9b3cda60c822dfadcaee66e0af" + integrity sha1-Skxd2OT7Xps82mDIIt+tyu5m4K8= + dependencies: + source-map "~0.4.0" + +inquirer@6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + integrity sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34= + dependencies: + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + figures "^1.3.5" + lodash "^4.3.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +inquirer@~6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" + integrity sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.11" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +insert-module-globals@^6.1.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-6.6.3.tgz#20638e29a30f9ed1ca2e3a825fbc2cba5246ddfc" + integrity sha1-IGOOKaMPntHKLjqCX7wsulJG3fw= + dependencies: + JSONStream "^1.0.3" + combine-source-map "~0.6.1" + concat-stream "~1.4.1" + is-buffer "^1.1.0" + lexical-scope "^1.2.0" + process "~0.11.0" + through2 "^1.0.0" + xtend "^4.0.0" + +install-artifact-from-github@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.2.0.tgz#adcbd123c16a4337ec44ea76d0ebf253cc16b074" + integrity sha512-3OxCPcY55XlVM3kkfIpeCgmoSKnMsz2A3Dbhsq0RXpIknKQmrX1YiznCeW9cD2ItFmDxziA3w6Eg8d80AoL3oA== + +interpret@^0.3.2: + version "0.3.10" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.3.10.tgz#088c25de731c6c5b112a90f0071cfaf459e5a7bb" + integrity sha1-CIwl3nMcbFsRKpDwBxz69Fnlp7s= + +interpret@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" + integrity sha1-1Xn7f2k7hYAElHrzn6DbSfeVYCw= + +interpret@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ= + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.2.0.tgz#8aba49c9192799585bdd643e0ccb50e8ae777ba4" + integrity sha1-irpJyRknmVhb3WQ+DMtQ6K53e6Q= + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-absolute@^0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" + integrity sha1-IN5p89uULvLYe5wto28XIjWxtes= + dependencies: + is-relative "^0.2.1" + is-windows "^0.2.0" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-array@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-array/-/is-array-1.0.1.tgz#e9850cc2cc860c3bc0977e84ccf0dd464584279a" + integrity sha1-6YUMwsyGDDvAl36EzPDdRkWEJ5o= + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" + integrity sha1-z8hszV3FpS+oBIkRHGkgxFfi2Ys= + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= + dependencies: + builtin-modules "^1.0.0" + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= + dependencies: + lower-case "^1.1.0" + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + integrity sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ== + +is-my-json-valid@^2.10.0: + version "2.20.5" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.5.tgz#5eca6a8232a687f68869b7361be1612e7512e5df" + integrity sha512-VTPuvvGQtxvCeghwspQu1rBgjYUT6FGxPlvFKbYuFtgc4ADsX3U5ihZOYN0qyU6u+d4X9xXb0IT5O6QpXKt87A== + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-odd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" + integrity sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ== + dependencies: + is-number "^4.0.0" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + integrity sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw= + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + integrity sha1-/AbloWg/vaE95mev9xe7wQpI838= + dependencies: + path-is-inside "^1.0.1" + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg== + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= + +is-promise@^2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-property@^1.0.0, is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= + +is-relative@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" + integrity sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU= + dependencies: + is-unc-path "^0.1.1" + +is-resolvable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" + integrity sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI= + dependencies: + tryit "^1.0.1" + +is-running@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-running/-/is-running-2.1.0.tgz#30a73ff5cc3854e4fc25490809e9f5abf8de09e0" + integrity sha1-MKc/9cw4VOT8JUkICen1q/jeCeA= + +is-stream-ended@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-stream-ended/-/is-stream-ended-0.1.4.tgz#f50224e95e06bce0e356d440a4827cd35b267eda" + integrity sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw== + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unc-path@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" + integrity sha1-arBTpyVzwQJQ/0FqOBTDUXivObk= + dependencies: + unc-path-regex "^0.1.0" + +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= + dependencies: + upper-case "^1.1.0" + +is-url@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" + integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== + +is-utf8@^0.2.0, is-utf8@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-windows@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" + integrity sha1-3hqm1j6indJIc3tp8f+LgALSEIw= + +is-windows@^1.0.1, is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +is-wsl@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" + integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== + +is2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.1.tgz#8ac355644840921ce435d94f05d3a94634d3481a" + integrity sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA== + dependencies: + deep-is "^0.1.3" + ip-regex "^2.1.0" + is-url "^1.2.2" + +isarray@0.0.1, isarray@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + +isbinaryfile@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" + integrity sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE= + +isexe@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" + integrity sha1-NvPiLmB1CSD15yQaR2qMakInWtA= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@0.1.x, isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + integrity sha1-jxDXl32NefL2/4YqgbBRPMslaGw= + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +jasmine-core@^2.8.0, jasmine-core@~2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" + integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4= + +jasmine-core@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.5.2.tgz#6f61bd79061e27f43e6f9355e44b3c6cab6ff297" + integrity sha1-b2G9eQYeJ/Q+b5NV5Es8bKtv8pc= + +jasmine-growl-reporter@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/jasmine-growl-reporter/-/jasmine-growl-reporter-0.2.1.tgz#d5f0a37b92f6a83fd5c6482b809495c90a8b55fe" + integrity sha1-1fCje5L2qD/VxkgrgJSVyQqLVf4= + dependencies: + growl "~1.7.0" + +jasmine-node@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jasmine-node/-/jasmine-node-2.0.0.tgz#81751a72325f5497490b14181a55087f1b0371ff" + integrity sha1-gXUacjJfVJdJCxQYGlUIfxsDcf8= + dependencies: + coffee-script "~1.7.1" + gaze "~0.5.1" + jasmine-growl-reporter "~0.2.0" + minimist "0.0.8" + mkdirp "~0.3.5" + underscore "~1.6.0" + walkdir "~0.0.7" + +jasmine-reporters@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/jasmine-reporters/-/jasmine-reporters-2.2.0.tgz#e8c7916df3e4283bc8829a3fc21140eb322f8a5b" + integrity sha1-6MeRbfPkKDvIgpo/whFA6zIvils= + dependencies: + jasmine "^2.4.1" + mkdirp "^0.5.1" + xmldom "^0.1.22" + +jasmine@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz#6b089c0a11576b1f16df11b80146d91d4e8b8a3e" + integrity sha1-awicChFXax8W3xG4AUbZHU6Lij4= + dependencies: + exit "^0.1.2" + glob "^7.0.6" + jasmine-core "~2.8.0" + +jasmine@^2.4.1: + version "2.5.3" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.5.3.tgz#5441f254e1fc2269deb1dfd93e0e57d565ff4d22" + integrity sha1-VEHyVOH8Imnesd/ZPg5X1WX/TSI= + dependencies: + exit "^0.1.2" + glob "^7.0.6" + jasmine-core "~2.5.2" + +jasminewd2@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.1.0.tgz#da595275d1ae631de736ac0a7c7d85c9f73ef652" + integrity sha1-2llSddGuYx3nNqwKfH2Fyfc+9lI= + +jju@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= + +join-path@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/join-path/-/join-path-1.1.1.tgz#10535a126d24cbd65f7ffcdf15ef2e631076b505" + integrity sha1-EFNaEm0ky9Zff/zfFe8uYxB2tQU= + dependencies: + as-array "^2.0.0" + url-join "0.0.1" + valid-url "^1" + +"jquery-2.1@npm:jquery@2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.1.4.tgz#228bde698a0c61431dc2630a6a154f15890d2317" + integrity sha1-IoveaYoMYUMdwmMKahVPFYkNIxc= + +"jquery-2.2@npm:jquery@2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02" + integrity sha1-LInWiJterFIqfuoywUUhVZxsvwI= + +jquery@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" + integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== + +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + integrity sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1, js-yaml@^3.5.1, js-yaml@~3.14.0: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-bigint@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" + integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== + dependencies: + bignumber.js "^9.0.0" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-parse-helpfulerror@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz#13f14ce02eed4e981297b64eb9e3b932e2dd13dc" + integrity sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w= + dependencies: + jju "^1.1.0" + +json-ptr@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/json-ptr/-/json-ptr-1.3.2.tgz#17f45b322a843b1f2fbcc9b45132bd9b3ba8cd38" + integrity sha512-tFH40YQ+lG7mgYYM1kGZOhQngO4SbOEHZJlA4W+NtetWZ20EUU3BPU+30uWRKumuAJoSo5eqrsXD2h72ioS8ew== + dependencies: + tslib "^2.0.0" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stable-stringify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" + integrity sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsonparse@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" + integrity sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ= + +jsonparse@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.0.tgz#85fc245b1d9259acc6941960b905adf64e7de0e8" + integrity sha1-hfwkWx2SWazGlBlguQWt9k594Og= + +jsonpointer@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.1.0.tgz#501fb89986a2389765ba09e6053299ceb4f2c2cc" + integrity sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg== + +jsonschema@^1.0.2: + version "1.2.6" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.6.tgz#52b0a8e9dc06bbae7295249d03e4b9faee8a0c0b" + integrity sha512-SqhURKZG07JyKKeo/ir24QnS4/BV7a6gQy93bUSe4lUdNp0QNpIz2c9elWJQ9dpc5cQYY6cvCzgRwy0MQCLyqA== + +jsonwebtoken@^8.2.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jszip@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.5.tgz#e3c2a6c6d706ac6e603314036d43cd40beefdf37" + integrity sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ== + dependencies: + core-js "~2.3.0" + es6-promise "~3.0.2" + lie "~3.1.0" + pako "~1.0.2" + readable-stream "~2.0.6" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwa@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" + integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +jws@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" + integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== + dependencies: + jwa "^2.0.0" + safe-buffer "^5.0.1" + +karma-browserstack-launcher@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/karma-browserstack-launcher/-/karma-browserstack-launcher-1.5.1.tgz#4caf4cd476a76d3c88205818d8994fc170a68fb4" + integrity sha512-zt9Ukow5A9WZHZXCFVO/h5kRsAdaZYeMNJK9Uan8v42amQXt3B/DZVxl24NCcAIxufKjW13UWd9iJ9knG9OCYw== + dependencies: + browserstack "~1.5.1" + browserstack-local "^1.3.7" + q "~1.5.0" + +karma-chrome-launcher@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" + integrity sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg== + dependencies: + which "^1.2.1" + +karma-edge-launcher@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/karma-edge-launcher/-/karma-edge-launcher-0.4.2.tgz#3d9529b09b13c909c5f3ceee12d00e7f9a989b3d" + integrity sha512-YAJZb1fmRcxNhMIWYsjLuxwODBjh2cSHgTW/jkVmdpGguJjLbs9ZgIK/tEJsMQcBLUkO+yO4LBbqYxqgGW2HIw== + dependencies: + edge-launcher "1.2.2" + +karma-firefox-launcher@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-1.2.0.tgz#64fe03dd10300f9754d48f9ebfbf31f6c94a200c" + integrity sha512-j9Zp8M8+VLq1nI/5xZGfzeaEPtGQ/vk3G+Y8vpmFWLvKLNZ2TDjD6cu2dUu7lDbu1HXNgatsAX4jgCZTkR9qhQ== + dependencies: + is-wsl "^2.1.0" + +karma-ie-launcher@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/karma-ie-launcher/-/karma-ie-launcher-1.0.0.tgz#497986842c490190346cd89f5494ca9830c6d59c" + integrity sha1-SXmGhCxJAZA0bNifVJTKmDDG1Zw= + dependencies: + lodash "^4.6.1" + +karma-jasmine@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.2.tgz#394f2b25ffb4a644b9ada6f22d443e2fd08886c3" + integrity sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM= + +karma-junit-reporter@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-junit-reporter/-/karma-junit-reporter-2.0.1.tgz#d34eef7f0b2fd064e0896954e8851a90cf14c8f3" + integrity sha512-VtcGfE0JE4OE1wn0LK8xxDKaTP7slN8DO3I+4xg6gAi1IoAHAXOJ1V9G/y45Xg6sxdxPOR3THCFtDlAfBo9Afw== + dependencies: + path-is-absolute "^1.0.0" + xmlbuilder "12.0.0" + +karma-safari-launcher@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz#96982a2cc47d066aae71c553babb28319115a2ce" + integrity sha1-lpgqLMR9BmquccVTursoMZEVos4= + +karma-sauce-launcher@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/karma-sauce-launcher/-/karma-sauce-launcher-2.0.2.tgz#dbf98e70d86bf287b03a537cf637eb7aefa975c3" + integrity sha512-jLUFaJhHMcKpxFWUesyWYihzM5FvQiJsDwGcCtKeOy2lsWhkVw0V0Byqb1d+wU6myU1mribBtsIcub23HS4kWA== + dependencies: + sauce-connect-launcher "^1.2.4" + saucelabs "^1.5.0" + selenium-webdriver "^4.0.0-alpha.1" + +karma-script-launcher@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz#cd017c4de5ef09e5a9da793276176108dd4b542d" + integrity sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0= + +karma-spec-reporter@0.0.32: + version "0.0.32" + resolved "https://registry.yarnpkg.com/karma-spec-reporter/-/karma-spec-reporter-0.0.32.tgz#2e9c7207ea726771260259f82becb543209e440a" + integrity sha1-LpxyB+pyZ3EmAln4K+y1QyCeRAo= + dependencies: + colors "^1.1.2" + +karma@4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/karma/-/karma-4.4.1.tgz#6d9aaab037a31136dc074002620ee11e8c2e32ab" + integrity sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A== + dependencies: + bluebird "^3.3.0" + body-parser "^1.16.1" + braces "^3.0.2" + chokidar "^3.0.0" + colors "^1.1.0" + connect "^3.6.0" + di "^0.0.1" + dom-serialize "^2.2.0" + flatted "^2.0.0" + glob "^7.1.1" + graceful-fs "^4.1.2" + http-proxy "^1.13.0" + isbinaryfile "^3.0.0" + lodash "^4.17.14" + log4js "^4.0.0" + mime "^2.3.1" + minimatch "^3.0.2" + optimist "^0.6.1" + qjobs "^1.1.4" + range-parser "^1.2.0" + rimraf "^2.6.0" + safe-buffer "^5.0.1" + socket.io "2.1.1" + source-map "^0.6.1" + tmp "0.0.33" + useragent "2.3.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== + +kuler@1.0.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6" + integrity sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ== + dependencies: + colornames "^1.1.1" + +labeled-stream-splicer@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-1.0.2.tgz#4615331537784981e8fd264e1f3a434c4e0ddd65" + integrity sha1-RhUzFTd4SYHo/SZOHzpDTE4N3WU= + dependencies: + inherits "^2.0.1" + isarray "~0.0.1" + stream-splicer "^1.1.0" + +latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== + dependencies: + package-json "^6.3.0" + +lazy-debug-legacy@0.0.X: + version "0.0.1" + resolved "https://registry.yarnpkg.com/lazy-debug-legacy/-/lazy-debug-legacy-0.0.1.tgz#537716c0776e4cf79e3ed1b621f7658c2911b1b1" + integrity sha1-U3cWwHduTPeePtG2IfdljCkRsbE= + +lazystream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= + dependencies: + readable-stream "^2.0.5" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lexical-scope@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4" + integrity sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ= + dependencies: + astw "^2.0.0" + +lie@~3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= + dependencies: + immediate "~3.0.5" + +liftoff@^2.0.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385" + integrity sha1-qY8v9nGD2Lp8+soQVIvX/wVQs4U= + dependencies: + extend "^3.0.0" + findup-sync "^0.4.2" + fined "^1.0.1" + flagged-respawn "^0.3.2" + lodash.isplainobject "^4.0.4" + lodash.isstring "^4.0.1" + lodash.mapvalues "^4.4.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +liftup@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/liftup/-/liftup-3.0.1.tgz#1cb81aff0f368464ed3a5f1a7286372d6b1a60ce" + integrity sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw== + dependencies: + extend "^3.0.2" + findup-sync "^4.0.0" + fined "^1.2.0" + flagged-respawn "^1.0.1" + is-plain-object "^2.0.4" + object.map "^1.0.1" + rechoir "^0.7.0" + resolve "^1.19.0" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + +listenercount@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" + integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= + +load-grunt-tasks@^3.5.0: + version "3.5.2" + resolved "https://registry.yarnpkg.com/load-grunt-tasks/-/load-grunt-tasks-3.5.2.tgz#0728561180fd20ff8a6927505852fc58aaea0c88" + integrity sha1-ByhWEYD9IP+KaSdQWFL8WKrqDIg= + dependencies: + arrify "^1.0.0" + multimatch "^2.0.0" + pkg-up "^1.0.0" + resolve-pkg "^0.1.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +lodash._basecopy@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= + +lodash._basetostring@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= + +lodash._basevalues@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= + +lodash._escapehtmlchar@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._escapehtmlchar/-/lodash._escapehtmlchar-2.4.1.tgz#df67c3bb6b7e8e1e831ab48bfa0795b92afe899d" + integrity sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0= + dependencies: + lodash._htmlescapes "~2.4.1" + +lodash._escapestringchar@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._escapestringchar/-/lodash._escapestringchar-2.4.1.tgz#ecfe22618a2ade50bfeea43937e51df66f0edb72" + integrity sha1-7P4iYYoq3lC/7qQ5N+Ud9m8O23I= + +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= + +lodash._htmlescapes@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._htmlescapes/-/lodash._htmlescapes-2.4.1.tgz#32d14bf0844b6de6f8b62a051b4f67c228b624cb" + integrity sha1-MtFL8IRLbeb4tioFG09nwii2JMs= + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= + +lodash._isnative@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._isnative/-/lodash._isnative-2.4.1.tgz#3ea6404b784a7be836c7b57580e1cdf79b14832c" + integrity sha1-PqZAS3hKe+g2x7V1gOHN95sUgyw= + +lodash._objecttypes@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._objecttypes/-/lodash._objecttypes-2.4.1.tgz#7c0b7f69d98a1f76529f890b0cdb1b4dfec11c11" + integrity sha1-fAt/admKH3ZSn4kLDNsbTf7BHBE= + +lodash._reescape@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= + +lodash._reevaluate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= + +lodash._reinterpolate@^2.4.1, lodash._reinterpolate@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-2.4.1.tgz#4f1227aa5a8711fc632f5b07a1f4607aab8b3222" + integrity sha1-TxInqlqHEfxjL1sHofRgequLMiI= + +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + +lodash._reunescapedhtml@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._reunescapedhtml/-/lodash._reunescapedhtml-2.4.1.tgz#747c4fc40103eb3bb8a0976e571f7a2659e93ba7" + integrity sha1-dHxPxAED6zu4oJduVx96JlnpO6c= + dependencies: + lodash._htmlescapes "~2.4.1" + lodash.keys "~2.4.1" + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= + +lodash._shimkeys@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash._shimkeys/-/lodash._shimkeys-2.4.1.tgz#6e9cc9666ff081f0b5a6c978b83e242e6949d203" + integrity sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM= + dependencies: + lodash._objecttypes "~2.4.1" + +lodash.assignwith@^4.0.7: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb" + integrity sha1-EnqX8CrcQXUalU0ksN4X4QDgOOs= + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= + +lodash.defaults@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-2.4.1.tgz#a7e8885f05e68851144b6e12a8f3678026bc4c54" + integrity sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ= + dependencies: + lodash._objecttypes "~2.4.1" + lodash.keys "~2.4.1" + +lodash.difference@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw= + +lodash.escape@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= + dependencies: + lodash._root "^3.0.0" + +lodash.escape@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-2.4.1.tgz#2ce12c5e084db0a57dda5e5d1eeeb9f5d175a3b4" + integrity sha1-LOEsXghNsKV92l5dHu659dF1o7Q= + dependencies: + lodash._escapehtmlchar "~2.4.1" + lodash._reunescapedhtml "~2.4.1" + lodash.keys "~2.4.1" + +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isarguments@2.4.x: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-2.4.1.tgz#4931a9c08253adf091ae7ca192258a973876ecca" + integrity sha1-STGpwIJTrfCRrnyhkiWKlzh27Mo= + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isempty@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isobject@^2.4.1, lodash.isobject@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-2.4.1.tgz#5a2e47fe69953f1ee631a7eba1fe64d2d06558f5" + integrity sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU= + dependencies: + lodash._objecttypes "~2.4.1" + +lodash.isobject@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" + integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= + +lodash.isplainobject@^4.0.4, lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.keys@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.keys@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-2.4.1.tgz#48dea46df8ff7632b10d706b8acb26591e2b3727" + integrity sha1-SN6kbfj/djKxDXBrissmWR4rNyc= + dependencies: + lodash._isnative "~2.4.1" + lodash._shimkeys "~2.4.1" + lodash.isobject "~2.4.1" + +lodash.map@^4.5.1: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + integrity sha1-dx7Hg540c9nEzeKLGTlMNWL09tM= + +lodash.mapvalues@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" + integrity sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw= + +lodash.memoize@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40= + +lodash.template@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-2.4.1.tgz#9e611007edf629129a974ab3c48b817b3e1cf20d" + integrity sha1-nmEQB+32KRKal0qzxIuBez4c8g0= + dependencies: + lodash._escapestringchar "~2.4.1" + lodash._reinterpolate "~2.4.1" + lodash.defaults "~2.4.1" + lodash.escape "~2.4.1" + lodash.keys "~2.4.1" + lodash.templatesettings "~2.4.1" + lodash.values "~2.4.1" + +lodash.template@^3.0.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= + dependencies: + lodash._basecopy "^3.0.0" + lodash._basetostring "^3.0.0" + lodash._basevalues "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + lodash.keys "^3.0.0" + lodash.restparam "^3.0.0" + lodash.templatesettings "^3.0.0" + +lodash.templatesettings@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.escape "^3.0.0" + +lodash.templatesettings@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-2.4.1.tgz#ea76c75d11eb86d4dbe89a83893bb861929ac699" + integrity sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk= + dependencies: + lodash._reinterpolate "~2.4.1" + lodash.escape "~2.4.1" + +lodash.toarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" + integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= + +lodash.union@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= + +lodash.values@^2.4.1, lodash.values@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-2.4.1.tgz#abf514436b3cb705001627978cbcf30b1280eea4" + integrity sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ= + dependencies: + lodash.keys "~2.4.1" + +lodash@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= + +lodash@^4.0.0, lodash@^4.13.1, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.6.1, lodash@^4.7.0, lodash@^4.8.0, lodash@~4.17.19, lodash@~4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lodash@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" + integrity sha1-j1dWDIO1n8JwvT1WG2kAQ0MOJVE= + +lodash@~2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" + integrity sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4= + +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +log4js@^0.6.27: + version "0.6.38" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" + integrity sha1-LElBFmldb7JUgJQ9P8hy5mKlIv0= + dependencies: + readable-stream "~1.0.2" + semver "~4.3.3" + +log4js@^4.0.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-4.5.1.tgz#e543625e97d9e6f3e6e7c9fc196dd6ab2cae30b5" + integrity sha512-EEEgFcE9bLgaYUKuozyFfytQM2wDHtXn4tAN41pkaxpNjAykv11GVdeI4tHtmPWW4Xrgh9R/2d7XYghDVjbKKw== + dependencies: + date-format "^2.0.0" + debug "^4.1.1" + flatted "^2.0.0" + rfdc "^1.1.4" + streamroller "^1.0.6" + +logform@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360" + integrity sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ== + dependencies: + colors "^1.2.1" + fast-safe-stringify "^2.0.4" + fecha "^2.3.3" + ms "^2.1.1" + triple-beam "^1.3.0" + +lolex@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" + integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE= + +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + +longest@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-2.0.1.tgz#781e183296aa94f6d4d916dc335d0d17aefa23f8" + integrity sha1-eB4YMpaqlPbU2RbcM10NF676I/g= + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.3.tgz#c92393d976793eee5ba4edb583cf8eae35bd9bfb" + integrity sha1-ySOT2XZ5Pu5bpO21g8+OrjW9m/s= + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.5.2.tgz#1fddad938aae1263ce138680be1b3f591c0ab41c" + integrity sha1-H92tk4quEmPOE4aAvhs/WRwKtBw= + +lru-cache@4.1.x: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + integrity sha1-HRdnnAac2l0ECZGgnbwsDbN35V4= + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lru-queue@0.1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= + dependencies: + es5-ext "~0.10.2" + +lunr@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-0.7.2.tgz#79a30e932e216cba163541ee37a3607c12cd7281" + integrity sha1-eaMOky4hbLoWNUHuN6NgfBLNcoE= + +make-dir@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-error-cause@^1.1.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" + integrity sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0= + dependencies: + make-error "^1.2.0" + +make-error@^1.2.0: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +make-iterator@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" + integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== + dependencies: + kind-of "^6.0.2" + +map-cache@^0.2.0, map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +marked-terminal@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-3.3.0.tgz#25ce0c0299285998c7636beaefc87055341ba1bd" + integrity sha512-+IUQJ5VlZoAFsM5MHNT7g3RHSkA3eETqhRCdXv4niUMAKHQ7lb1yvAcuGPmm4soxhmtX13u4Li6ZToXtvSEH+A== + dependencies: + ansi-escapes "^3.1.0" + cardinal "^2.1.1" + chalk "^2.4.1" + cli-table "^0.3.1" + node-emoji "^1.4.1" + supports-hyperlinks "^1.0.1" + +marked@^0.3.2, marked@~0.3.0: + version "0.3.6" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" + integrity sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc= + +marked@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" + integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memoizee@^0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" + integrity sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg== + dependencies: + d "1" + es5-ext "^0.10.45" + es6-weak-map "^2.0.2" + event-emitter "^0.3.5" + is-promise "^2.1" + lru-queue "0.1" + next-tick "1" + timers-ext "^0.1.5" + +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merge@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-2.1.1.tgz#59ef4bf7e0b3e879186436e8481c06a6c162ca98" + integrity sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^2.3.7: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + integrity sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0= + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.44.0, "mime-db@>= 1.43.0 < 2": + version "1.44.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" + integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + integrity sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE= + +mime-db@~1.37.0: + version "1.37.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" + integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== + +mime-types@^2.1.12, mime-types@~2.1.18: + version "2.1.21" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" + integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== + dependencies: + mime-db "~1.37.0" + +mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.27" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" + integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + dependencies: + mime-db "1.44.0" + +mime-types@~2.1.11, mime-types@~2.1.15: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + integrity sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo= + dependencies: + mime-db "~1.30.0" + +mime@1.3.4, mime@^1.2.11: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + integrity sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM= + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" + integrity sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg== + +mime@~1.2.11: + version "1.2.11" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" + integrity sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA= + +mimeparse@^0.1.4, mimeparse@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/mimeparse/-/mimeparse-0.1.4.tgz#dafb02752370fd226093ae3152c271af01ac254a" + integrity sha1-2vsCdSNw/SJgk64xUsJxrwGsJUo= + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" + integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +"minimatch@2 || 3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + integrity sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q= + dependencies: + brace-expansion "^1.0.0" + +minimatch@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-1.0.0.tgz#e0dd2120b49e1b724ce8d714c520822a9438576d" + integrity sha1-4N0hILSeG3JM6NcUxSCCKpQ4V20= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@^2.0.1: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + integrity sha1-jQh8OcazjAAbl/ynzm0OHoCvusc= + dependencies: + brace-expansion "^1.0.0" + +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimatch@~0.2.11: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + integrity sha1-x054BXT2PG+aCQ6Q775u9TpqdWo= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minimist@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.2.0.tgz#4dffe525dae2b864c66c2e23c6271d7afdecefce" + integrity sha1-Tf/lJdriuGTGbC4jxicdev3s784= + +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" + integrity sha512-/jAn9/tEX4gnpyRATxgHEOV6xbcyxgT7iUnxo9Y3+OB0zX00TgKIv/2FZCf5brBbICcwbLqVv2ImjvWWrQMSYw== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minipass@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + integrity sha512-4T6Ur/GctZ27nHfpt9THOdRZNgyJ9FZchYO1ceg5S8Q3DNLCKYy44nCZzgCJgcvx2UM8czmqak5BCxJMrq37lA== + dependencies: + minipass "^2.2.1" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*, mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + integrity sha1-G79asbqCevI1dRQ0kEJkVfSB/h4= + +mkdirp@^1.0.3, mkdirp@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" + integrity sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc= + +mocha@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +module-deps@^3.6.3: + version "3.9.1" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-3.9.1.tgz#ea75caf9199090d25b0d5512b5acacb96e7f87f3" + integrity sha1-6nXK+RmQkNJbDVUStaysuW5/h/M= + dependencies: + JSONStream "^1.0.3" + browser-resolve "^1.7.0" + concat-stream "~1.4.5" + defined "^1.0.0" + detective "^4.0.0" + duplexer2 "0.0.2" + inherits "^2.0.1" + parents "^1.0.0" + readable-stream "^1.1.13" + resolve "^1.1.3" + stream-combiner2 "~1.0.0" + subarg "^1.0.0" + through2 "^1.0.0" + xtend "^4.0.0" + +moment@^2.20.1, moment@^2.8.4: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +morgan@^1.10.0, morgan@^1.8.2: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + +morgan@^1.6.1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.8.0.tgz#7884d7816b7e5a22db11a9e3a572b197a0e5566c" + integrity sha1-eITXgWt+WiLbEanjpXKxl6DlVmw= + dependencies: + basic-auth "~1.1.0" + debug "2.6.0" + depd "~1.1.0" + on-finished "~2.3.0" + on-headers "~1.0.1" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + integrity sha1-riXPJRKziFodldfwN4aNhDESR2U= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multimatch@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" + integrity sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis= + dependencies: + array-differ "^1.0.0" + array-union "^1.0.1" + arrify "^1.0.0" + minimatch "^3.0.0" + +multipipe@^0.1.0, multipipe@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= + dependencies: + duplexer2 "0.0.2" + +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + integrity sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA= + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nan@^2.12.1, nan@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" + integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== + +nan@^2.14.2: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +nanomatch@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" + integrity sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-odd "^2.0.0" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +napi-build-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.1.tgz#1381a0f92c39d66bf19852e7873432fc2123e508" + integrity sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA== + +nash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/nash/-/nash-3.0.0.tgz#bced3a0cb8434c2ad30d1a0d567cfc0c37128eea" + integrity sha512-M5SahEycXUmko3zOvsBkF6p94CWLhnyy9hfpQ9Qzp+rQkQ8D1OaTlfTl1OBWktq9Fak3oDXKU+ev7tiMaMu+1w== + dependencies: + async "^1.3.0" + flat-arguments "^1.0.0" + lodash "^4.17.5" + minimist "^1.1.0" + +natives@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.6.tgz#a603b4a498ab77173612b9ea1acdec4d980f00bb" + integrity sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +needle@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + integrity sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +netmask@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= + +next-tick@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081" + integrity sha1-euuhxzpSGEJlVUt9wDuvcg34AIE= + dependencies: + lower-case "^1.1.1" + +node-abi@^2.7.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.12.0.tgz#40e9cfabdda1837863fa825e7dfa0b15686adf6f" + integrity sha512-VhPBXCIcvmo/5K8HPmnWJyyhvgKxnHTUMXR/XwGHV68+wrgkzST4UmQrY/XszSWA5dtnXpNp528zkcyJ/pzVcw== + dependencies: + semver "^5.4.1" + +node-emoji@^1.4.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" + integrity sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw== + dependencies: + lodash.toarray "^4.4.0" + +node-fetch@^2.3.0, node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-forge@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + +node-gyp@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" + integrity sha512-CbpcIo7C3eMu3dL1c3d0xw449fHIGALIJsRP4DDPHpyiW8vcriNY7ubh9TE4zEKfSxscY7PjeFnshE7h75ynjQ== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.3" + nopt "^5.0.0" + npmlog "^4.1.2" + request "^2.88.2" + rimraf "^3.0.2" + semver "^7.3.2" + tar "^6.0.2" + which "^2.0.2" + +node-html-encoder@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/node-html-encoder/-/node-html-encoder-0.0.2.tgz#8973618d727da5526a830b47d07c0d803e0a15c6" + integrity sha1-iXNhjXJ9pVJqgwtH0HwNgD4KFcY= + +node-pre-gyp@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149" + integrity sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= + dependencies: + abbrev "1" + +nopt@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" + integrity sha1-jZJPFClg4Xd+f/4XBUNjHMfLAt8= + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +npm-bundled@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" + integrity sha512-ByQ3oJ/5ETLyglU2+8dBObvhfWXX8dtPZDMePCahptliFX2iIuhyEszyFk401PZUNQH20vvdW5MLjJxkwU80Ow== + +npm-packlist@^1.1.6: + version "1.1.10" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.10.tgz#1039db9e985727e464df066f4cf0ab6ef85c398a" + integrity sha512-AQC0Dyhzn4EiYEfIUjCdMl0JJ61I2ER9ukf/sLxJUcZHfo+VyEfz2rMJgLZSS1v30OxPQe1cN0LZA1xbcaVfWA== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-path@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.3.tgz#15cff4e1c89a38da77f56f6055b24f975dfb2bbe" + integrity sha1-Fc/04ciaONp39W9gVbJPl137K74= + dependencies: + which "^1.2.10" + +npm-run-path@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-1.0.0.tgz#f5c32bf595fe81ae927daec52e82f8b000ac3c8f" + integrity sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8= + dependencies: + path-key "^1.0.0" + +npm-run@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/npm-run/-/npm-run-4.1.0.tgz#a14e0ec854d8e837cc871495eb3644dde8a2f46b" + integrity sha1-oU4OyFTY6DfMhxSV6zZE3eii9Gs= + dependencies: + cross-spawn "^4.0.0" + minimist "^1.2.0" + npm-path "^2.0.2" + npm-which "^3.0.1" + serializerr "^1.0.2" + sync-exec "^0.6.2" + +npm-which@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/npm-which/-/npm-which-3.0.1.tgz#9225f26ec3a285c209cae67c3b11a6b4ab7140aa" + integrity sha1-kiXybsOihcIJyuZ8OxGmtKtxQKo= + dependencies: + commander "^2.9.0" + npm-path "^2.0.2" + which "^1.2.10" + +npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +nunjucks-date@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/nunjucks-date/-/nunjucks-date-1.2.0.tgz#187a2821984cfa7c2fba5ad3ed81d2249691a9ff" + integrity sha1-GHooIZhM+nwvulrT7YHSJJaRqf8= + dependencies: + moment "^2.8.4" + +nunjucks@^3.0.1, nunjucks@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-3.2.0.tgz#53e95f43c9555e822e8950008a201b1002d49933" + integrity sha512-YS/qEQ6N7qCnUdm6EoYRBfJUdWNT0PpKbbRnogV2XyXbBm2STIP1O6yrdZHgwMVK7fIYUx7i8+yatEixnXSB1w== + dependencies: + a-sync-waterfall "^1.0.0" + asap "^2.0.3" + yargs "^3.32.0" + optionalDependencies: + chokidar "^2.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + integrity sha1-ejs9DpgGPUP0wD8uiubNUahog6A= + +object-assign@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= + +object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.defaults@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" + integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= + dependencies: + array-each "^1.0.1" + array-slice "^1.0.0" + for-own "^1.0.0" + isobject "^3.0.0" + +object.map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" + integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= + dependencies: + for-own "^1.0.0" + make-iterator "^1.0.0" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.2.0, object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +objectdiff@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/objectdiff/-/objectdiff-1.1.0.tgz#8d7a15be6cb8670df8a490cc6be12a4f05ea82f4" + integrity sha1-jXoVvmy4Zw34pJDMa+EqTwXqgvQ= + +on-finished@^2.2.0, on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@^1.0.0, on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +on-headers@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c= + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +once@~1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + integrity sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA= + dependencies: + wrappy "1" + +one-time@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e" + integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4= + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + integrity sha1-ofeDj4MUxRbwXs78vEzP4EtO14k= + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +open-sans-fontface@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/open-sans-fontface/-/open-sans-fontface-1.4.0.tgz#03cc6d1bf5e6a8b5b47910888562f722c5dd3428" + integrity sha1-A8xtG/XmqLW0eRCIhWL3IsXdNCg= + +open@^6.3.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== + dependencies: + is-wsl "^1.1.0" + +openapi3-ts@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/openapi3-ts/-/openapi3-ts-1.4.0.tgz#679d5a24be0efc36f5de4fc2c4b8513663e16f65" + integrity sha512-8DmE2oKayvSkIR3XSZ4+pRliBsx19bSNeIzkTPswY8r4wvjX86bMxsORdqwAwMxE8PefOcSAT2auvi/0TZe9yA== + +opn@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" + integrity sha1-erwi5kTf9jsKltWrfyeQwPAavJU= + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +optimist@^0.6.1, optimist@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optimist@~0.3.5: + version "0.3.7" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.3.7.tgz#c90941ad59e4273328923074d2cf2e7cbc6ec0d9" + integrity sha1-yQlBrVnkJzMokjB00s8ufLxuwNk= + dependencies: + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +ora@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + +orchestrator@^0.3.0: + version "0.3.8" + resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" + integrity sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4= + dependencies: + end-of-stream "~0.1.5" + sequencify "~0.0.7" + stream-consume "~0.1.0" + +ordered-read-streams@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz#fd565a9af8eb4473ba69b6ed8a34352cb552f126" + integrity sha1-/VZamvjrRHO6abbtijQ1LLVS8SY= + +os-browserify@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" + integrity sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ= + +os-homedir@^1.0.0, os-homedir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + integrity sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ= + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-defer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" + integrity sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw== + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pac-proxy-agent@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-4.1.0.tgz#66883eeabadc915fc5e95457324cb0f0ac78defb" + integrity sha512-ejNgYm2HTXSIYX9eFlkvqFp8hyJ374uDf0Zq5YUAifiSh1D6fo+iBivQZirGvVv8dCYUsLhmLBRhlAYvBKI5+Q== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + get-uri "3" + http-proxy-agent "^4.0.1" + https-proxy-agent "5" + pac-resolver "^4.1.0" + raw-body "^2.2.0" + socks-proxy-agent "5" + +pac-resolver@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-4.1.0.tgz#4b12e7d096b255a3b84e53f6831f32e9c7e5fe95" + integrity sha512-d6lf2IrZJJ7ooVHr7BfwSjRO1yKSJMaiiWYSHcrxSIUtZrCa4KKGwcztdkZ/E9LFleJfjoi1yl+XLR7AX24nbQ== + dependencies: + degenerator "^2.2.0" + ip "^1.1.5" + netmask "^1.0.6" + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= + +pako@~1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== + +param-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.0.tgz#2619f90fd6c829ed0b958f1c84ed03a745a6d70a" + integrity sha1-Jhn5D9bIKe0LlY8chO0Dp0Wm1wo= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parents@^1.0.0, parents@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" + integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= + dependencies: + path-platform "~0.11.15" + +parents@~0.0.1: + version "0.0.3" + resolved "https://registry.yarnpkg.com/parents/-/parents-0.0.3.tgz#fa212f024d9fa6318dbb6b4ce676c8be493b9c43" + integrity sha1-+iEvAk2fpjGNu2tM5nbIvkk7nEM= + dependencies: + path-platform "^0.0.1" + +parse-asn1@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" + integrity sha1-NQYPbVAV03Yox3D04JGgtaJ4vCM= + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-filepath@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" + integrity sha1-FZ1hVdQ5BNFsEO9piRHaHpGWm3M= + dependencies: + is-absolute "^0.2.3" + map-cache "^0.2.0" + path-root "^0.1.1" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + +parseurl@~1.3.1, parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.0.tgz#39c248bde5a8dc02d5160696bdb01e044d016ee1" + integrity sha1-OcJIveWo3ALVFgaWvbAeBE0BbuE= + dependencies: + camel-case "^3.0.0" + upper-case-first "^1.1.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= + +path-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.0.tgz#5ac491de642936e5dfe0e18d16c461b8be8cf073" + integrity sha1-WsSR3mQpNuXf4OGNFsRhuL6M8HM= + dependencies: + no-case "^2.2.0" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-1.0.0.tgz#5d53d578019646c0d68800db4e146e6bdc2ac7af" + integrity sha1-XVPVeAGWRsDWiADbThRua9wqx68= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-platform@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.0.1.tgz#b5585d7c3c463d89aa0060d86611cf1afd617e2a" + integrity sha1-tVhdfDxGPYmqAGDYZhHPGv1hfio= + +path-platform@~0.11.15: + version "0.11.15" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= + dependencies: + path-root-regex "^0.1.0" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-to-regexp@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + +pbkdf2@^3.0.3: + version "3.0.9" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + integrity sha1-8sSyWmAAWLPDdzwIbDfbvuH/5pM= + dependencies: + create-hmac "^1.1.2" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picomatch@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" + integrity sha512-OYMyqkKzK7blWO/+XZYP6w8hH0LDvkBvdvKukti+7kqYFCiEAk+gI3DWnryapc0Dau05ugGTy0foQ6mqn4AHYA== + +picomatch@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + +picomatch@^2.2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +pkg-up@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-1.0.0.tgz#3e08fb461525c4421624a33b9f7e6d0af5b05a26" + integrity sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY= + dependencies: + find-up "^1.0.0" + +plist@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.1.tgz#a9b931d17c304e8912ef0ba3bdd6182baf2e1f8c" + integrity sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ== + dependencies: + base64-js "^1.2.3" + xmlbuilder "^9.0.7" + xmldom "0.1.x" + +pluralize@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" + integrity sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU= + +portfinder@^1.0.23: + version "1.0.26" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.26.tgz#475658d56ca30bed72ac7f1378ed350bd1b64e70" + integrity sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.1" + +portscanner@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/portscanner/-/portscanner-1.2.0.tgz#b14bbda257d14c310fa9cc09682af02d40961802" + integrity sha1-sUu9olfRTDEPqcwJaCrwLUCWGAI= + dependencies: + async "1.5.2" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +prebuild-install@^5.3.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.3.tgz#ef4052baac60d465f5ba6bf003c9c1de79b9da8e" + integrity sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== + dependencies: + detect-libc "^1.0.3" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + napi-build-utils "^1.0.1" + node-abi "^2.7.0" + noop-logger "^0.1.1" + npmlog "^4.0.1" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^3.0.3" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + which-pm-runs "^1.0.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= + +pretty-bytes@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" + integrity sha1-sr+C5zUNZcbDOqlaqlpPYyf2HNk= + +pretty-hrtime@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-0.2.2.tgz#d4fd88351e3a4741f8173af7d6a4b846f9895c00" + integrity sha1-1P2INR46R0H4Fzr31qS4RvmJXAA= + +process-nextick-args@^1.0.6, process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/process/-/process-0.8.0.tgz#7bbaf7187fe6ded3fd5be0cb6103fba9cacb9798" + integrity sha1-e7r3GH/m3tP9W+DLYQP7qcrLl5g= + +process@~0.11.0: + version "0.11.9" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" + integrity sha1-e9WtIapiU+fahoImTx4R0RwDGME= + +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= + +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-breaker@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/promise-breaker/-/promise-breaker-5.0.0.tgz#58e8541f1619554057da95a211794d7834d30c1d" + integrity sha512-mgsWQuG4kJ1dtO6e/QlNDLFtMkMzzecsC69aI5hlLEjGHFNpHrvGhFi4LiK5jg2SMQj74/diH+wZliL9LpGsyA== + +promises-aplus-tests@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/promises-aplus-tests/-/promises-aplus-tests-2.1.2.tgz#76b7c5638968720861969cfbcd8795afd274885c" + integrity sha1-drfFY4locghhlpz7zYeVr9J0iFw= + dependencies: + mocha "^2.5.3" + sinon "^1.10.3" + underscore "~1.8.3" + +protobufjs@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b" + integrity sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" "^13.7.0" + long "^4.0.0" + +protobufjs@^6.8.6: + version "6.9.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.9.0.tgz#c08b2bf636682598e6fabbf0edb0b1256ff090bd" + integrity sha512-LlGVfEWDXoI/STstRDdZZKb/qusoAWUnmLg9R8OLSO473mBLWHowx8clbX5/+mKDEI+v7GzjoK9tRPZMMcoTrg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" "^13.7.0" + long "^4.0.0" + +protochain@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/protochain/-/protochain-1.0.5.tgz#991c407e99de264aadf8f81504b5e7faf7bfa260" + integrity sha1-mRxAfpneJkqt+PgVBLXn+ve/omA= + +protractor@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/protractor/-/protractor-7.0.0.tgz#c3e263608bd72e2c2dc802b11a772711a4792d03" + integrity sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw== + dependencies: + "@types/q" "^0.0.32" + "@types/selenium-webdriver" "^3.0.0" + blocking-proxy "^1.0.0" + browserstack "^1.5.1" + chalk "^1.1.3" + glob "^7.0.3" + jasmine "2.8.0" + jasminewd2 "^2.1.0" + q "1.4.1" + saucelabs "^1.5.0" + selenium-webdriver "3.6.0" + source-map-support "~0.4.0" + webdriver-js-extender "2.1.0" + webdriver-manager "^12.1.7" + yargs "^15.3.1" + +proxy-addr@~1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.3.tgz#dc97502f5722e888467b3fa2297a7b1ff47df074" + integrity sha1-3JdQL1ci6IhGez+iKXp7H/R98HQ= + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.2.0" + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +proxy-agent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-4.0.1.tgz#326c3250776c7044cd19655ccbfadf2e065a045c" + integrity sha512-ODnQnW2jc/FUVwHHuaZEfN5otg/fMbvMxz9nMSUQfJ9JU7q2SZvSULSsjLloVgJOiv9yhc8GlNMKc4GkFmcVEA== + dependencies: + agent-base "^6.0.0" + debug "4" + http-proxy-agent "^4.0.0" + https-proxy-agent "^5.0.0" + lru-cache "^5.1.1" + pac-proxy-agent "^4.1.0" + proxy-from-env "^1.0.0" + socks-proxy-agent "^5.0.0" + +proxy-from-env@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +ps-tree@=1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.1.tgz#5f1ba35455b8c25eeb718d04c37de1555d96d3db" + integrity sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A== + dependencies: + event-stream "=3.3.4" + +pseudomap@^1.0.1, pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + integrity sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY= + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +punycode@~1.2.3: + version "1.2.4" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.2.4.tgz#54008ac972aec74175def9cba6df7fa9d3918740" + integrity sha1-VACKyXKux0F13vnLpt9/qdORh0A= + +pupa@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" + integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== + dependencies: + escape-goat "^2.0.0" + +q-io@^1.10.9: + version "1.13.2" + resolved "https://registry.yarnpkg.com/q-io/-/q-io-1.13.2.tgz#eea130d481ddb5e1aa1bc5a66855f7391d06f003" + integrity sha1-7qEw1IHdteGqG8WmaFX3OR0G8AM= + dependencies: + collections "^0.2.0" + mime "^1.2.11" + mimeparse "^0.1.4" + q "^1.0.1" + qs "^1.2.1" + url2 "^0.0.0" + +q-io@~1.10.6: + version "1.10.9" + resolved "https://registry.yarnpkg.com/q-io/-/q-io-1.10.9.tgz#90473f797398eb1208b7edd468ed1785ad7baacd" + integrity sha1-kEc/eXOY6xIIt+3UaO0Xha17qs0= + dependencies: + collections "~0.2.0" + mime "~1.2.11" + mimeparse "~0.1.4" + q "~0.9.7" + qs "~0.1.0" + url2 "~0.0.0" + +q@0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/q/-/q-0.8.4.tgz#662b6d97db73e141c96529ce2f10670b65ef01b0" + integrity sha1-Zittl9tz4UHJZSnOLxBnC2XvAbA= + +q@1.4.1, q@^1.0.1, q@^1.4.1, q@~1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + integrity sha1-VXBbzZPF82c1MMLCy8DCs63cKG4= + +q@~0.9.7: + version "0.9.7" + resolved "https://registry.yarnpkg.com/q/-/q-0.9.7.tgz#4de2e6cb3b29088c9e4cbc03bf9d42fb96ce2f75" + integrity sha1-TeLmyzspCIyeTLwDv51C+5bOL3U= + +q@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.0.1.tgz#11872aeedee89268110b10a718448ffb10112a14" + integrity sha1-EYcq7t7okmgRCxCnGESP+xARKhQ= + +q@~1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= + +qjobs@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" + integrity sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM= + +qq@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/qq/-/qq-0.3.5.tgz#80a3018ce9b2969d7c44eb80cc5bc9eb2b04c7cc" + integrity sha1-gKMBjOmylp18ROuAzFvJ6ysEx8w= + dependencies: + q "0.8.4" + +qs@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" + integrity sha1-O3hIwDwt7OaalSKw+ujEEm10Xzs= + +qs@6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-1.2.2.tgz#19b57ff24dc2a99ce1f8bdf6afcda59f8ef61f88" + integrity sha1-GbV/8k3CqZzh+L32r82ln472H4g= + +qs@^6.6.0: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + +qs@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-0.1.0.tgz#9a0d2d70d01f63d3401ea4b050822601b462ee6b" + integrity sha1-mg0tcNAfY9NAHqSwUIImAbRi7ms= + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystring-es3@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +randomatic@^1.1.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + integrity sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + integrity sha1-Z0yZdgkBw8QRJ3GjHlIdw0nMCew= + +range-parser@^1.2.0, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k= + dependencies: + bytes "3.0.0" + http-errors "1.6.2" + iconv-lite "0.4.19" + unpipe "1.0.0" + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@^2.2.0, raw-body@^2.3.3: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.7, rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +re2@^1.15.8: + version "1.15.9" + resolved "https://registry.yarnpkg.com/re2/-/re2-1.15.9.tgz#9ed16171edcb0bc4f0e239bf55229ff3f26acbe3" + integrity sha512-AXWEhpMTBdC+3oqbjdU07dk0pBCvxh5vbOMLERL6Y8FYBSGn4vXlLe8cYszn64Yy7H8keVMrgPzoSvOd4mePpg== + dependencies: + install-artifact-from-github "^1.2.0" + nan "^2.14.2" + node-gyp "^7.1.2" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@1.1, readable-stream@1.1.x, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.0.27-1, readable-stream@^1.0.33-1, readable-stream@^1.1.13, readable-stream@^1.1.13-1, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@2 || 3", readable-stream@^3.0.1, readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.2: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@~2.0.0, readable-stream@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-wrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/readable-wrap/-/readable-wrap-1.0.0.tgz#3b5a211c631e12303a54991c806c17e7ae206bff" + integrity sha1-O1ohHGMeEjA6VJkcgGwX564ga/8= + dependencies: + readable-stream "^1.1.13-1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg= + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + +readdirp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" + integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== + dependencies: + picomatch "^2.2.1" + +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + integrity sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + +rechoir@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca" + integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q== + dependencies: + resolve "^1.9.0" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redeyed@~2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" + integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= + dependencies: + esprima "~4.0.0" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +registry-auth-token@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" + integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== + dependencies: + rc "^1.2.8" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + +replace-ext@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= + +replace-ext@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= + +request@^2.87.0, request@^2.88.0, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +require-uncached@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-dir@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" + integrity sha1-shklmlYC+sXFxJatiUpujMQwJh4= + dependencies: + expand-tilde "^1.2.2" + global-modules "^0.2.3" + +resolve-dir@^1.0.0, resolve-dir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= + +resolve-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-global@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-global/-/resolve-global-1.0.0.tgz#a2a79df4af2ca3f49bf77ef9ddacd322dad19255" + integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== + dependencies: + global-dirs "^0.1.1" + +resolve-pkg@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/resolve-pkg/-/resolve-pkg-0.1.0.tgz#02cc993410e2936962bd97166a1b077da9725531" + integrity sha1-AsyZNBDik2livZcWahsHfalyVTE= + dependencies: + resolve-from "^2.0.0" + +resolve-url@^0.2.1, resolve-url@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@1.1.7, resolve@^1.1.3, resolve@^1.1.6, resolve@^1.1.7, resolve@~1.1.0: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= + +resolve@^1.19.0, resolve@^1.9.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +resolve@~0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.3.1.tgz#34c63447c664c70598d1c9b126fc43b2a24310a4" + integrity sha1-NMY0R8ZkxwWY0cmxJvxDsqJDEKQ= + +resolve@~0.7.1: + version "0.7.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.7.4.tgz#395a9ef9e873fbfe12bd14408bd91bb936003d69" + integrity sha1-OVqe+ehz+/4SvRRAi9kbuTYAPWk= + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + integrity sha1-NGYfRohjJ/7SmRR5FSJS35LapUE= + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry-request@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/retry-request/-/retry-request-4.1.1.tgz#f676d0db0de7a6f122c048626ce7ce12101d2bd8" + integrity sha512-BINDzVtLI2BDukjWmjAIRZ0oglnCAkpP2vQjM3jdLhmT62h0xnQgciPwBRDAvHqpkPT2Wo1XuUyLyn6nbGrZQQ== + dependencies: + debug "^4.1.1" + through2 "^3.0.1" + +rewire@~2.1.0: + version "2.1.5" + resolved "https://registry.yarnpkg.com/rewire/-/rewire-2.1.5.tgz#764599179cae5e393839bf3ad6e0be371ee49d81" + integrity sha1-dkWZF5yuXjk4Ob861uC+Nx7knYE= + +rfdc@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" + integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== + +rfile@~1.0, rfile@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rfile/-/rfile-1.0.0.tgz#59708cf90ca1e74c54c3cfc5c36fdb9810435261" + integrity sha1-WXCM+Qyh50xUw8/Fw2/bmBBDUmE= + dependencies: + callsite "~1.0.0" + resolve "~0.3.0" + +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== + dependencies: + glob "^7.0.5" + +rimraf@^3.0.0, rimraf@^3.0.2, rimraf@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@~2.5.2: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + integrity sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ= + dependencies: + glob "^7.0.5" + +ripemd160@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" + integrity sha1-k6S71JQrxXS2mo+lfHHeEOzKfW4= + +router@^1.3.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/router/-/router-1.3.5.tgz#cb2f47f74fd99a77fb3bc01cc947f46b79b1790f" + integrity sha512-kozCJZUhuSJ5VcLhSb3F8fsmGXy+8HaDbKCAerR1G6tq3mnMZFMuSohbFvGv1c5oMFipijDjRZuuN/Sq5nMf3g== + dependencies: + array-flatten "3.0.0" + debug "2.6.9" + methods "~1.1.2" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + setprototypeof "1.2.0" + utils-merge "1.0.1" + +rsvp@^4.8.5: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +ruglify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ruglify/-/ruglify-1.0.0.tgz#dc8930e2a9544a274301cc9972574c0d0986b675" + integrity sha1-3Ikw4qlUSidDAcyZcldMDQmGtnU= + dependencies: + rfile "~1.0" + uglify-js "~2.2" + +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + integrity sha1-yK1KXhEGYeQCp9IbUw4AnyX444k= + dependencies: + once "^1.3.0" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI= + +rx@~2.3.20: + version "2.3.25" + resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.25.tgz#2f7c0550532777b41fa692bb790a7886eaff9731" + integrity sha1-L3wFUFMnd7QfppK7eQp4hur/lzE= + +rxjs@^6.4.0: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +samsam@1.1.2, samsam@~1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" + integrity sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc= + +sauce-connect-launcher@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sauce-connect-launcher/-/sauce-connect-launcher-1.2.4.tgz#8d38f85242a9fbede1b2303b559f7e20c5609a1c" + integrity sha512-X2vfwulR6brUGiicXKxPm1GJ7dBEeP1II450Uv4bHGrcGOapZNgzJvn9aioea5IC5BPp/7qjKdE3xbbTBIVXMA== + dependencies: + adm-zip "~0.4.3" + async "^2.1.2" + https-proxy-agent "^2.2.1" + lodash "^4.16.6" + rimraf "^2.5.4" + +"sauce-connect@https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz": + version "0.0.0" + resolved "https://saucelabs.com/downloads/sc-4.6.2-linux.tar.gz#7b7f35433af9c3380758e048894d7b9aecf3754e" + +saucelabs@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.5.0.tgz#9405a73c360d449b232839919a86c396d379fd9d" + integrity sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ== + dependencies: + https-proxy-agent "^2.2.1" + +sax@>=0.6.0, sax@^1.1.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" + integrity sha1-/YYxojvHgmvvXYcb24c3jJVkeCg= + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +selenium-webdriver@3.6.0, selenium-webdriver@^3.0.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz#2ba87a1662c020b8988c981ae62cb2a01298eafc" + integrity sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q== + dependencies: + jszip "^3.1.3" + rimraf "^2.5.4" + tmp "0.0.30" + xml2js "^0.4.17" + +selenium-webdriver@^4.0.0-alpha.1: + version "4.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.1.tgz#cc93415e21d2dc1dfd85dfc5f6b55f3ac53933b1" + integrity sha512-z88rdjHAv3jmTZ7KSGUkTvo4rGzcDGMq0oXWHNIDK96Gs31JKVdu9+FMtT4KBrVoibg8dUicJDok6GnqqttO5Q== + dependencies: + jszip "^3.1.3" + rimraf "^2.5.4" + tmp "0.0.30" + xml2js "^0.4.17" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== + dependencies: + semver "^6.3.0" + +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= + +semver@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-2.2.1.tgz#7941182b3ffcc580bff1c17942acdf7951c0d213" + integrity sha1-eUEYKz/8xYC/8cF5QqzfeVHA0hM= + +semver@^4.1.0, semver@~4.3.3: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= + +semver@^5.0.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^5.3.0, semver@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.0.0, semver@^7.1.3, semver@^7.3.2: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +send@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.14.2.tgz#39b0438b3f510be5dc6f667a11f71689368cdeef" + integrity sha1-ObBDiz9RC+Xcb2Z6EfcWiTaM3u8= + dependencies: + debug "~2.2.0" + depd "~1.1.0" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + fresh "0.3.0" + http-errors "~1.5.1" + mime "1.3.4" + ms "0.7.2" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.1" + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +sentence-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.0.tgz#d592fbed457fd1a59e3af0ee17e99f6fd70d7efd" + integrity sha1-1ZL77UV/0aWeOvDuF+mfb9cNfv0= + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + +sequencify@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/sequencify/-/sequencify-0.0.7.tgz#90cff19d02e07027fd767f5ead3e7b95d1e7380c" + integrity sha1-kM/xnQLgcCf9dn9erT57ldHnOAw= + +serializerr@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/serializerr/-/serializerr-1.0.3.tgz#12d4c5aa1c3ffb8f6d1dc5f395aa9455569c3f91" + integrity sha1-EtTFqhw/+49tHcXzlaqUVVacP5E= + dependencies: + protochain "^1.0.5" + +serve-favicon@^2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/serve-favicon/-/serve-favicon-2.3.2.tgz#dd419e268de012ab72b319d337f2105013f9381f" + integrity sha1-3UGeJo3gEqtysxnTN/IQUBP5OB8= + dependencies: + etag "~1.7.0" + fresh "0.3.0" + ms "0.7.2" + parseurl "~1.3.1" + +serve-index@^1.7.1, serve-index@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b" + integrity sha1-fF2WwT+xMRAfk8HFd0+FFqHnjTs= + dependencies: + accepts "~1.3.3" + batch "0.5.3" + debug "~2.2.0" + escape-html "~1.0.3" + http-errors "~1.5.0" + mime-types "~2.1.11" + parseurl "~1.3.1" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +serve-static@^1.10.0, serve-static@^1.11.1, serve-static@~1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.2.tgz#2cf9889bd4435a320cc36895c9aa57bd662e6ac7" + integrity sha1-LPmIm9RDWjIMw2iVyapXvWYuasc= + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.14.2" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" + integrity sha1-gaVSFB7BBLiOic44MQOtXGZWTQg= + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.3.6, sha.js@~2.4.4: + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" + integrity sha1-NwaMLEdra69ALRSknGf1l5IfY08= + dependencies: + inherits "^2.0.1" + +shallow-copy@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + integrity sha1-QV9CcC1z2BAzApLMXuhurhoRoXA= + +shasum@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" + integrity sha1-5wEjENj0F/TetXEhUOVni4euVl8= + dependencies: + json-stable-stringify "~0.0.0" + sha.js "~2.4.4" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-0.0.1.tgz#1a41196f3c0333c482323593d6886ecf153dd986" + integrity sha1-GkEZbzwDM8SCMjWT1ohuzxU92YY= + +shelljs@^0.7.0, shelljs@^0.7.4, shelljs@^0.7.5: + version "0.7.6" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.6.tgz#379cccfb56b91c8601e4793356eb5382924de9ad" + integrity sha1-N5zM+1a5HIYB5HkzVutTgpJN6a0= + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" + integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + +simple-get@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.0.tgz#b45be062435e50d159540b576202ceec40b9c6b3" + integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + +simple-node-logger@^0.93.42: + version "0.93.42" + resolved "https://registry.yarnpkg.com/simple-node-logger/-/simple-node-logger-0.93.42.tgz#ebc6893ef27a838f12dbd16a71e694976cb0f975" + integrity sha512-3Kh0egP+iYr/TSDOdbLEMgyd9o6XsBMk3xk4Ds0/dwoclgDOqdOygusBixcr6ls0nG5xTpwgWiatWfZXjCiCLA== + dependencies: + lodash "^4.17.10" + moment "^2.20.1" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sinon@^1.10.3: + version "1.17.7" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf" + integrity sha1-RUKk9JugxFwF6y6d2dID4rjv4L8= + dependencies: + formatio "1.1.1" + lolex "1.3.2" + samsam "1.1.2" + util ">=0.10.3 <1" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= + +smart-buffer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" + integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +socket.io-adapter@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b" + integrity sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs= + +socket.io-client@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.1.1.tgz#dcb38103436ab4578ddb026638ae2f21b623671f" + integrity sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~3.1.0" + engine.io-client "~3.2.0" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.2.0" + to-array "0.1.4" + +socket.io-parser@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.2.0.tgz#e7c6228b6aa1f814e6148aea325b51aa9499e077" + integrity sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + +socket.io@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.1.1.tgz#a069c5feabee3e6b214a75b40ce0652e1cfb9980" + integrity sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA== + dependencies: + debug "~3.1.0" + engine.io "~3.2.0" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.1.1" + socket.io-parser "~3.2.0" + +socks-proxy-agent@5, socks-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.0.tgz#7c0f364e7b1cf4a7a437e71253bed72e9004be60" + integrity sha512-lEpa1zsWCChxiynk+lCycKuC502RxDWLKJZoIhnxrWNjLSDGYRFflHA1/228VkRcnv9TIb8w98derGbpKxJRgA== + dependencies: + agent-base "6" + debug "4" + socks "^2.3.3" + +socks@^2.3.3: + version "2.5.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f" + integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + +sorted-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-1.0.0.tgz#5d1f4f9c1fb2cd48965967304e212eb44cfb6d05" + integrity sha1-XR9PnB+yzUiWWWcwTiEutEz7bQU= + +source-map-resolve@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.3.1.tgz#610f6122a445b8dd51535a2a71b783dfc1248761" + integrity sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E= + dependencies: + atob "~1.1.0" + resolve-url "~0.2.1" + source-map-url "~0.3.0" + urix "~0.1.0" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== + dependencies: + source-map "^0.5.6" + +source-map-support@~0.4.0: + version "0.4.11" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" + integrity sha1-ZH+TmXizhTWQlTCIUwPa8jJ58yI= + dependencies: + source-map "^0.5.3" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= + +source-map-url@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" + integrity sha1-fsrxO1e80J2opAxdJp2zN5nUqvk= + +source-map@0.1.34: + version "0.1.34" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.34.tgz#a7cfe89aec7b1682c3b198d0acfb47d7d090566b" + integrity sha1-p8/omux7FoLDsZjQrPtH19CQVms= + dependencies: + amdefine ">=0.0.4" + +source-map@0.X, source-map@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + +source-map@^0.1.38, source-map@~0.1.31, source-map@~0.1.7: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.3, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.3.0.tgz#8586fb9a5a005e5b501e21cd18b6f21b457ad1f9" + integrity sha1-hYb7mloAXltQHiHNGLbyG0V60fk= + dependencies: + amdefine ">=0.0.4" + +source-map@~0.4.0, source-map@~0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + integrity sha1-66T12pwNyZneaAMti092FzZSA2s= + dependencies: + amdefine ">=0.0.4" + +sparkles@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" + integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= + +spawnback@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/spawnback/-/spawnback-1.0.0.tgz#f73662f7e54d95367eca74d6426c677dd7ea686f" + integrity sha1-9zZi9+VNlTZ+ynTWQmxnfdfqaG8= + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + integrity sha1-SzBz2TP/UfORLwOsVRlJikFQ20A= + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + integrity sha1-m98vIOH0DtRH++JzJmGR/O1RYmw= + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + integrity sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc= + +spdx-license-list@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-license-list/-/spdx-license-list-2.1.0.tgz#3788ffb5c80b24afbe8283934e9e6684ea6a218d" + integrity sha1-N4j/tcgLJK++goOTTp5mhOpqIY0= + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split@0.2: + version "0.2.10" + resolved "https://registry.yarnpkg.com/split/-/split-0.2.10.tgz#67097c601d697ce1368f418f06cd201cf0521a57" + integrity sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc= + dependencies: + through "2" + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + +sprintf-js@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.15.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.1.tgz#b79a089a732e346c6e0714830f36285cd38191a2" + integrity sha512-mSdgNUaidk+dRU5MhYtN9zebdzF2iG0cNPWy8HG+W8y+fT1JnSkh0fzzpjOa0L7P8i1Rscz38t0h4gPcKz43xA== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-trace@0.0.x: + version "0.0.9" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" + integrity sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU= + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.3.1 < 2": + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.3.0, statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4= + +stream-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-1.0.0.tgz#bf9b4abfb42b274d751479e44e0ff2656b6f1193" + integrity sha1-v5tKv7QrJ011FHnkTg/yZWtvEZM= + dependencies: + inherits "~2.0.1" + readable-stream "^1.0.27-1" + +stream-buffers@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/stream-buffers/-/stream-buffers-2.2.0.tgz#91d5f5130d1cef96dcfa7f726945188741d09ee4" + integrity sha1-kdX1Ew0c75bc+n9yaUUYh0HQnuQ= + +stream-combiner2@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.0.2.tgz#ba72a6b50cbfabfa950fc8bc87604bd01eb60671" + integrity sha1-unKmtQy/q/qVD8i8h2BL0B62BnE= + dependencies: + duplexer2 "~0.0.2" + through2 "~0.5.1" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + +stream-consume@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stream-consume/-/stream-consume-0.1.0.tgz#a41ead1a6d6081ceb79f65b061901b6d8f3d1d0f" + integrity sha1-pB6tGm1ggc63n2WwYZAbbY89HQ8= + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +stream-splicer@^1.1.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-1.3.2.tgz#3c0441be15b9bf4e226275e6dc83964745546661" + integrity sha1-PARBvhW5v04iYnXm3IOWR0VUZmE= + dependencies: + indexof "0.0.1" + inherits "^2.0.1" + isarray "~0.0.1" + readable-stream "^1.1.13-1" + readable-wrap "^1.0.0" + through2 "^1.0.0" + +streamroller@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-1.0.6.tgz#8167d8496ed9f19f05ee4b158d9611321b8cacd9" + integrity sha512-3QC47Mhv3/aZNFpDDVO44qQb9gwB9QggMEE0sQmkTAwBVYdBRWISdsywlkfm5II1Q5y/pmrHflti/IgmIzdDBg== + dependencies: + async "^2.6.2" + date-format "^2.0.0" + debug "^3.2.6" + fs-extra "^7.0.1" + lodash "^4.17.14" + +string-length@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" + integrity sha1-VpcPscOFWOnnC3KL894mmsRa36w= + dependencies: + strip-ansi "^3.0.0" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + integrity sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4= + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^3.0.0" + +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.0, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +stringmap@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" + integrity sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE= + +strip-ansi@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" + integrity sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA= + dependencies: + ansi-regex "^0.2.1" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-bom@2.X, strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-bom@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-1.0.0.tgz#85b8862f3844b5a6d5ec8467a93598173a36f794" + integrity sha1-hbiGLzhEtabV7IRnqTWYFzo295Q= + dependencies: + first-chunk-stream "^1.0.0" + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= + dependencies: + minimist "^1.1.0" + +superstatic@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/superstatic/-/superstatic-7.1.0.tgz#42cc773a0f500fb691841e0533d0b8c31f25997f" + integrity sha512-yBU8iw07nM3Bu4jFc8lnKwLey0cj61OaGmFJZcYC2X+kEpXVmXzERJ3OTAHZAESe1OTeNIuWadt81U5IULGGAA== + dependencies: + basic-auth-connect "^1.0.0" + chalk "^1.1.3" + compare-semver "^1.0.0" + compression "^1.7.0" + connect "^3.6.2" + destroy "^1.0.4" + fast-url-parser "^1.1.3" + fs-extra "^8.1.0" + glob-slasher "^1.0.1" + home-dir "^1.0.0" + is-url "^1.2.2" + join-path "^1.1.1" + lodash "^4.17.19" + mime-types "^2.1.16" + minimatch "^3.0.4" + morgan "^1.8.2" + nash "^3.0.0" + on-finished "^2.2.0" + on-headers "^1.0.0" + path-to-regexp "^1.8.0" + router "^1.3.1" + rsvp "^4.8.5" + string-length "^1.0.0" + update-notifier "^4.1.1" + optionalDependencies: + re2 "^1.15.8" + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + integrity sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4= + +supports-color@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" + integrity sha1-2S3iaU6z9nMjlz1649i1W0wiGQo= + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= + +supports-color@^5.0.0, supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" + integrity sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw== + dependencies: + has-flag "^2.0.0" + supports-color "^5.0.0" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + +sync-exec@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/sync-exec/-/sync-exec-0.6.2.tgz#717d22cc53f0ce1def5594362f3a89a2ebb91105" + integrity sha1-cX0izFPwzh3vVZQ2LzqJouu5EQU= + +syntax-error@^1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.1.6.tgz#b4549706d386cc1c1dc7c2423f18579b6cade710" + integrity sha1-tFSXBtOGzBwdx8JCPxhXm2yt5xA= + dependencies: + acorn "^2.7.0" + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + integrity sha1-K7xULw/amGGnVdOUf+/Ys/UThV8= + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +tar-fs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" + integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== + dependencies: + chownr "^1.1.1" + mkdirp "^0.5.1" + pump "^3.0.0" + tar-stream "^2.0.0" + +tar-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" + integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== + dependencies: + bl "^1.0.0" + buffer-alloc "^1.2.0" + end-of-stream "^1.0.0" + fs-constants "^1.0.0" + readable-stream "^2.3.0" + to-buffer "^1.1.1" + xtend "^4.0.0" + +tar-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" + integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== + dependencies: + bl "^3.0.0" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar-stream@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.2.tgz#6d5ef1a7e5783a95ff70b69b97455a5968dc1325" + integrity sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q== + dependencies: + bl "^4.0.1" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@^4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" + integrity sha512-mq9ixIYfNF9SK0IS/h2HKMu8Q2iaCuhDDsZhdEag/FHv8fOaYld4vN7ouMgcSSt5WKZzPs8atclTcJm36OTh4w== + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +tar@^4.3.0: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +tar@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +tcp-port-used@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70" + integrity sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q== + dependencies: + debug "4.1.0" + is2 "2.0.1" + +temp-fs@^0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/temp-fs/-/temp-fs-0.9.9.tgz#8071730437870720e9431532fe2814364f8803d7" + integrity sha1-gHFzBDeHByDpQxUy/igUNk+IA9c= + dependencies: + rimraf "~2.5.2" + +term-size@^2.1.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" + integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through2@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" + integrity sha1-OE51MU1J8y3hLuu4E2uOtrXVnak= + dependencies: + readable-stream "~2.0.0" + xtend "~4.0.0" + +through2@2.X, through2@^2.0.0, through2@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +through2@^0.5.0, through2@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.5.1.tgz#dfdd012eb9c700e2323fd334f38ac622ab372da7" + integrity sha1-390BLrnHAOIyP9M084rGIqs3Lac= + dependencies: + readable-stream "~1.0.17" + xtend "~3.0.0" + +through2@^0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + integrity sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg= + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + +through2@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-1.1.1.tgz#0847cbc4449f3405574dbdccd9bb841b83ac3545" + integrity sha1-CEfLxESfNAVXTb3M2buEG4OsNUU= + dependencies: + readable-stream ">=1.1.13-1 <1.2.0-0" + xtend ">=4.0.0 <4.1.0-0" + +through2@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" + integrity sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww== + dependencies: + readable-stream "2 || 3" + +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tildify@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/tildify/-/tildify-1.2.0.tgz#dcec03f55dca9b7aa3e5b04f21817eb56e63588a" + integrity sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo= + dependencies: + os-homedir "^1.0.0" + +time-stamp@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.0.1.tgz#9f4bd23559c9365966f3302dbba2b07c6b99b151" + integrity sha1-n0vSNVnJNllm8zAtu6KwfGuZsVE= + +timers-browserify@^1.0.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= + dependencies: + process "~0.11.0" + +timers-ext@^0.1.5: + version "0.1.7" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.7.tgz#6f57ad8578e07a3fb9f91d9387d65647555e25c6" + integrity sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ== + dependencies: + es5-ext "~0.10.46" + next-tick "1" + +title-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.0.tgz#c68ccb4232079ded64f94b91b4941ade91391979" + integrity sha1-xozLQjIHne1k+UuRtJQa3pE5GXk= + dependencies: + no-case "^2.2.0" + upper-case "^1.0.3" + +tmp@0.0.30: + version "0.0.30" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" + integrity sha1-ckGdSovn1s51FI/YsyTlk6cRwu0= + dependencies: + os-tmpdir "~1.0.1" + +tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + +to-buffer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" + integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + integrity sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +toxic@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toxic/-/toxic-1.0.1.tgz#8c2e2528da591100adc3883f2c0e56acfb1c7288" + integrity sha512-WI3rIGdcaKULYg7KVoB0zcjikqvcYYvcuT6D89bFPz2rVR0Rl0PK6x8/X62rtdLtBKIE985NzVf/auTtGegIIg== + dependencies: + lodash "^4.17.10" + +traceur@vojtajina/traceur-compiler#add-es6-pure-transformer-dist: + version "0.0.33" + resolved "https://codeload.github.com/vojtajina/traceur-compiler/tar.gz/d90b1e34c799bf61cd1aafdc33db0a554fa9e617" + dependencies: + commander ">=1.1" + q-io "~1.10.6" + semver "2.2.1" + +"traverse@>=0.3.0 <0.4": + version "0.3.9" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" + integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + +triple-beam@^1.2.0, triple-beam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" + integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== + +tryit@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" + integrity sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics= + +tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +tslib@^2.0.0, tslib@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + +tty-browserify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +tweetnacl@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +tweetsodium@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/tweetsodium/-/tweetsodium-0.0.5.tgz#f63ab4b1d26d6355d82d512a2bbf03cae96eb3e8" + integrity sha512-T3aXZtx7KqQbutTtBfn+P5By3HdBuB1eCoGviIrRJV2sXeToxv2X2cv5RvYqgG26PSnN5m3fYixds22Gkfd11w== + dependencies: + blakejs "^1.1.0" + tweetnacl "^1.0.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@~1.6.14, type-is@~1.6.15: + version "1.6.15" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + integrity sha1-yrEPtJCeRByChC6v4a1kbIGARBA= + dependencies: + media-typer "0.3.0" + mime-types "~2.1.15" + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.0.0.tgz#5f16ff6ef2eb44f260494dae271033b29c09a9c3" + integrity sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@~0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" + integrity sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw== + +uglify-js@^3.0.5: + version "3.13.9" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.9.tgz#4d8d21dcd497f29cfd8e9378b9df123ad025999b" + integrity sha512-wZbyTQ1w6Y7fHdt8sJnHfSIuWeDgk6B5rCb4E/AM6QNNPbOMIZph21PW5dRB3h7Df0GszN+t7RuUH6sWK5bF0g== + +uglify-js@~2.2: + version "2.2.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.2.5.tgz#a6e02a70d839792b9780488b7b8b184c095c99c7" + integrity sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc= + dependencies: + optimist "~0.3.5" + source-map "~0.1.7" + +uglify-js@~2.4.0: + version "2.4.24" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.4.24.tgz#fad5755c1e1577658bb06ff9ab6e548c95bebd6e" + integrity sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4= + dependencies: + async "~0.2.6" + source-map "0.1.34" + uglify-to-browserify "~1.0.0" + yargs "~3.5.4" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +umd@^2.1.0, umd@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/umd/-/umd-2.1.0.tgz#4a6307b762f17f02d201b5fa154e673396c263cf" + integrity sha1-SmMHt2LxfwLSAbX6FU5nM5bCY88= + dependencies: + rfile "~1.0.0" + ruglify "~1.0.0" + through "~2.3.4" + uglify-js "~2.4.0" + +unc-path-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= + +underscore-contrib@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/underscore-contrib/-/underscore-contrib-0.3.0.tgz#665b66c24783f8fa2b18c9f8cbb0e2c7d48c26c7" + integrity sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc= + dependencies: + underscore "1.6.0" + +underscore.string@~3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023" + integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg== + dependencies: + sprintf-js "^1.0.3" + util-deprecate "^1.0.2" + +underscore@1.6.0, underscore@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + integrity sha1-izixDKze9jM3uLJOT/htRa6lKag= + +underscore@^1.6.0, underscore@~1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + integrity sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI= + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +unique-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" + integrity sha1-1ZpKdUJ0R9mqbJHnAmP40mpLEEs= + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +universal-analytics@^0.4.16: + version "0.4.20" + resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.20.tgz#d6b64e5312bf74f7c368e3024a922135dbf24b03" + integrity sha512-gE91dtMvNkjO+kWsPstHRtSwHXz0l2axqptGYp5ceg4MsuurloM0PU3pdOfpb5zBXUvyjT4PwhWK2m39uczZuw== + dependencies: + debug "^3.0.0" + request "^2.88.0" + uuid "^3.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +unzipper@^0.10.10: + version "0.10.11" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.10.11.tgz#0b4991446472cbdb92ee7403909f26c2419c782e" + integrity sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "^1.0.12" + graceful-fs "^4.2.2" + listenercount "~1.0.1" + readable-stream "~2.3.6" + setimmediate "~1.0.4" + +unzipper@^0.9.3: + version "0.9.15" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.9.15.tgz#97d99203dad17698ee39882483c14e4845c7549c" + integrity sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA== + dependencies: + big-integer "^1.6.17" + binary "~0.3.0" + bluebird "~3.4.1" + buffer-indexof-polyfill "~1.0.0" + duplexer2 "~0.1.4" + fstream "^1.0.12" + listenercount "~1.0.1" + readable-stream "~2.3.6" + setimmediate "~1.0.4" + +upath@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== + +update-notifier@^4.1.0, update-notifier@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" + integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== + dependencies: + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +upper-case-first@^1.1.0, upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0, urix@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-join@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-0.0.1.tgz#1db48ad422d3402469a87f7d97bdebfe4fb1e3c8" + integrity sha1-HbSK1CLTQCRpqH99l73r/k+x48g= + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url2@^0.0.0, url2@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/url2/-/url2-0.0.0.tgz#4eaabd1d5c3ac90d62ab4485c998422865a04b1a" + integrity sha1-Tqq9HVw6yQ1iq0SFyZhCKGWgSxo= + +url@~0.10.1: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +urlencode@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/urlencode/-/urlencode-1.1.0.tgz#1f2ba26f013c85f0133f7a3ad6ff2730adf7cbb7" + integrity sha1-HyuibwE8hfATP3o61v8nMK33y7c= + dependencies: + iconv-lite "~0.4.11" + +use@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.0.tgz#14716bf03fdfefd03040aef58d8b4b85f3a7c544" + integrity sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw== + dependencies: + kind-of "^6.0.2" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= + +user-home@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + integrity sha1-nHC/2Babwdy/SGBODwS4tJzenp8= + dependencies: + os-homedir "^1.0.0" + +useragent@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972" + integrity sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw== + dependencies: + lru-cache "4.1.x" + tmp "0.0.x" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@0.10.3, "util@>=0.10.3 <1", util@~0.10.1: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + integrity sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.0, uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +v8flags@^2.0.2: + version "2.0.11" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" + integrity sha1-vKjzDw1tYGEswsAGQeaWLUKuaIE= + dependencies: + user-home "^1.1.1" + +v8flags@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" + integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== + dependencies: + homedir-polyfill "^1.0.1" + +valid-url@^1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" + integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + integrity sha1-KAS6vnEq0zeUWaz74kdGqywwP7w= + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +validate.js@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/validate.js/-/validate.js-0.9.0.tgz#8acf0144f1520a19835c6cc663f45e0836aa56c8" + integrity sha1-is8BRPFSChmDXGzGY/ReCDaqVsg= + +vary@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" + integrity sha1-4eWv+70WrnaN0mdDlLmtMCJlMUA= + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vinyl-fs@^0.3.0: + version "0.3.14" + resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-0.3.14.tgz#9a6851ce1cac1c1cea5fe86c0931d620c2cfa9e6" + integrity sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY= + dependencies: + defaults "^1.0.0" + glob-stream "^3.1.5" + glob-watcher "^0.0.6" + graceful-fs "^3.0.0" + mkdirp "^0.5.0" + strip-bom "^1.0.0" + through2 "^0.6.1" + vinyl "^0.4.0" + +vinyl-sourcemaps-apply@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" + integrity sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU= + dependencies: + source-map "^0.5.1" + +vinyl@1.X: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" + integrity sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ= + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vinyl@^0.2.1: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.2.3.tgz#bca938209582ec5a49ad538a00fa1f125e513252" + integrity sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI= + dependencies: + clone-stats "~0.0.1" + +vinyl@^0.4.0: + version "0.4.6" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" + integrity sha1-LzVsh6VQolVGHza76ypbqL94SEc= + dependencies: + clone "^0.2.0" + clone-stats "^0.0.1" + +vinyl@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= + dependencies: + clone "^1.0.0" + clone-stats "^0.0.1" + replace-ext "0.0.1" + +vinyl@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.0.1.tgz#1c3b4931e7ac4c1efee743f3b91a74c094407bb6" + integrity sha1-HDtJMeesTB7+50PzuRp0wJRAe7Y= + dependencies: + clone "^1.0.0" + clone-buffer "^1.0.0" + clone-stats "^1.0.0" + cloneable-readable "^1.0.0" + is-stream "^1.1.0" + remove-trailing-separator "^1.0.1" + replace-ext "^1.0.0" + +vm-browserify@~0.0.1: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= + dependencies: + indexof "0.0.1" + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= + +walkdir@^0.0.11, walkdir@~0.0.7: + version "0.0.11" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532" + integrity sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI= + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + +weak-map@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/weak-map/-/weak-map-1.0.0.tgz#b66e56a9df0bd25a76bbf1b514db129080614a37" + integrity sha1-tm5Wqd8L0lp2u/G1FNsSkIBhSjc= + +webdriver-js-extender@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz#57d7a93c00db4cc8d556e4d3db4b5db0a80c3bb7" + integrity sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ== + dependencies: + "@types/selenium-webdriver" "^3.0.0" + selenium-webdriver "^3.0.1" + +webdriver-manager@^12.1.7: + version "12.1.7" + resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162" + integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA== + dependencies: + adm-zip "^0.4.9" + chalk "^1.1.1" + del "^2.2.0" + glob "^7.0.3" + ini "^1.3.4" + minimist "^1.2.0" + q "^1.4.1" + request "^2.87.0" + rimraf "^2.5.2" + semver "^5.3.0" + xml2js "^0.4.17" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-pm-runs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" + integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= + +which@^1.2.1, which@^1.2.10, which@^1.2.12, which@^1.2.9: + version "1.2.12" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" + integrity sha1-3me15FAmnxlJCe8j7OTr5Bb6EZI= + dependencies: + isexe "^1.1.1" + +which@^1.2.14: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2, which@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + integrity sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w== + dependencies: + string-width "^1.0.2" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= + +window-size@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY= + +winston-transport@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.3.0.tgz#df68c0c202482c448d9b47313c07304c2d7c2c66" + integrity sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A== + dependencies: + readable-stream "^2.3.6" + triple-beam "^1.2.0" + +winston@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119" + integrity sha1-C0hCDZeMAYBM8CMLZIhhWYIloRk= + dependencies: + async "~1.0.0" + colors "1.0.x" + cycle "1.0.x" + eyes "0.1.x" + isstream "0.1.x" + stack-trace "0.0.x" + +winston@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07" + integrity sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw== + dependencies: + async "^2.6.1" + diagnostics "^1.1.1" + is-stream "^1.1.0" + logform "^2.1.1" + one-time "0.0.4" + readable-stream "^3.1.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.3.0" + +word-wrap@^1.0.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.1.tgz#248f459b465d179a17bc407c854d3151d07e45d8" + integrity sha1-JI9Fm0ZdF5oXvEB8hU0xUdB+Rdg= + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= + dependencies: + mkdirp "^0.5.1" + +ws@^7.2.3: + version "7.3.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd" + integrity sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w== + +ws@~3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== + +xml2js@^0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" + integrity sha1-F76T6q4/O3eTWceVtBlwWogX6Gg= + dependencies: + sax ">=0.6.0" + xmlbuilder "^4.1.0" + +xmlbuilder@12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-12.0.0.tgz#e2ed675e06834a089ddfb84db96e2c2b03f78c1a" + integrity sha512-lMo8DJ8u6JRWp0/Y4XLa/atVDr75H9litKlb2E5j3V3MesoL50EBgZDWoLT3F/LztVnG67GjPXLZpqcky/UMnQ== + +xmlbuilder@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" + integrity sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU= + dependencies: + lodash "^4.0.0" + +xmlbuilder@^9.0.7: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + +xmldom@0.1.x: + version "0.1.31" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + +xmldom@^0.1.22: + version "0.1.27" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" + integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= + +xmlhttprequest-ssl@~1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.4.tgz#04f560915724b389088715cc0ed7813e9677bf57" + integrity sha1-BPVgkVcks4kIhxXMDteBPpZ3v1c= + +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= + +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@^3.0.0, xtend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" + integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo= + +y18n@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" + integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.0.0.tgz#306c543835f09ee1a4cb23b7bce9ab341c91cdd4" + integrity sha1-MGxUODXwnuGkyyO3vOmrNByRzdQ= + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k= + +yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^18.1.1: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.3.1: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.1" + +yargs@^3.32.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" + integrity sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU= + dependencies: + camelcase "^2.0.1" + cliui "^3.0.3" + decamelize "^1.1.1" + os-locale "^1.4.0" + string-width "^1.0.1" + window-size "^0.1.4" + y18n "^3.2.0" + +yargs@~3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.5.4.tgz#d8aff8f665e94c34bd259bdebd1bfaf0ddd35361" + integrity sha1-2K/49mXpTDS9JZvevRv68N3TU2E= + dependencies: + camelcase "^1.0.2" + decamelize "^1.0.0" + window-size "0.1.0" + wordwrap "0.0.2" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= + +zip-stream@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04" + integrity sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ= + dependencies: + archiver-utils "^1.3.0" + compress-commons "^1.2.0" + lodash "^4.8.0" + readable-stream "^2.0.0" + +zip-stream@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-2.1.3.tgz#26cc4bdb93641a8590dd07112e1f77af1758865b" + integrity sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q== + dependencies: + archiver-utils "^2.1.0" + compress-commons "^2.1.1" + readable-stream "^3.4.0"