├── .buildkite └── pipeline.yml ├── .circleci └── config.yml ├── .dockerignore ├── .github └── workflows │ ├── add-issue-to-triage-board.yml │ ├── chrome-docker.yml │ ├── chrome.yml │ ├── parallel.yml │ ├── semantic-pull-request.yml │ ├── single.yml │ ├── triage_closed_issue_comment.yml │ └── using-action.yml ├── .gitignore ├── .gitlab-ci.yml ├── .husky ├── install.mjs └── pre-commit ├── .node-version ├── .npmrc ├── .release.json ├── .semaphore └── semaphore.yml ├── .travis.yml ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── Dockerfile ├── Jenkinsfile ├── LICENSE ├── README.md ├── amplify.yml ├── app ├── assets │ ├── css │ │ ├── styles.css │ │ ├── todo.css │ │ └── vendor │ │ │ ├── bootstrap.min.css │ │ │ ├── bootstrap.min.css.map │ │ │ └── fira.css │ ├── fonts │ │ ├── eot │ │ │ ├── FiraSans-Medium.eot │ │ │ └── FiraSans-Regular.eot │ │ ├── ttf │ │ │ ├── FiraSans-Medium.ttf │ │ │ └── FiraSans-Regular.ttf │ │ └── woff │ │ │ ├── FiraSans-Medium.woff │ │ │ └── FiraSans-Regular.woff │ ├── img │ │ ├── favicon.ico │ │ └── javascript-logo.png │ └── js │ │ ├── scripts.js │ │ ├── todo │ │ ├── app.js │ │ ├── controller.js │ │ ├── helpers.js │ │ ├── model.js │ │ ├── store.js │ │ ├── template.js │ │ └── view.js │ │ └── vendor │ │ ├── bootstrap.min.js │ │ ├── highlight.pack.js │ │ ├── jquery-1.12.0.min.js │ │ └── jquery-1.12.0.min.map ├── commands │ ├── actions.html │ ├── aliasing.html │ ├── assertions.html │ ├── connectors.html │ ├── cookies.html │ ├── files.html │ ├── location.html │ ├── misc.html │ ├── navigation.html │ ├── network-requests.html │ ├── querying.html │ ├── spies-stubs-clocks.html │ ├── storage.html │ ├── traversal.html │ ├── viewport.html │ ├── waiting.html │ └── window.html ├── cypress-api.html ├── index.html ├── todo.html └── utilities.html ├── appveyor.yml ├── azure-ci.yml ├── basic ├── .circleci │ └── config.yml ├── .gitlab-ci.yml ├── .semaphore.yml ├── .travis.yml ├── Jenkinsfile ├── README.md ├── azure-ci.yml ├── buildspec.yml └── codeship-pro │ ├── Dockerfile │ ├── README.md │ ├── codeship-services.yml │ └── codeship-steps.yml ├── buddy.yml ├── buildspec.yml ├── codeship-services.yml ├── codeship-steps.yml ├── cypress.config.js ├── cypress ├── e2e │ ├── 1-getting-started │ │ └── todo.cy.js │ └── 2-advanced-examples │ │ ├── actions.cy.js │ │ ├── aliasing.cy.js │ │ ├── assertions.cy.js │ │ ├── connectors.cy.js │ │ ├── cookies.cy.js │ │ ├── cypress_api.cy.js │ │ ├── files.cy.js │ │ ├── location.cy.js │ │ ├── misc.cy.js │ │ ├── navigation.cy.js │ │ ├── network_requests.cy.js │ │ ├── querying.cy.js │ │ ├── spies_stubs_clocks.cy.js │ │ ├── storage.cy.js │ │ ├── traversal.cy.js │ │ ├── utilities.cy.js │ │ ├── viewport.cy.js │ │ ├── waiting.cy.js │ │ └── window.cy.js ├── fixtures │ └── example.json └── support │ ├── commands.js │ └── e2e.js ├── env.encrypted ├── eslint.config.mjs ├── package-lock.json ├── package.json ├── renovate.json ├── scripts ├── set-port.js └── start.js └── serve.json /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | env: 2 | TERM: "xterm" 3 | 4 | steps: 5 | - label: ":cypress: Cypress" 6 | command: | 7 | echo "--- Node version" 8 | node --version 9 | 10 | echo "--- npm version" 11 | npm --version 12 | 13 | echo "--- Install npm dependencies" 14 | npm install 15 | 16 | # if necessary, reinstall "correct" version of Cypress 17 | # a common situation if testing preview binary build 18 | # npx cypress install --force 19 | # 20 | echo "--- Install custom Cypress version" 21 | npm install "$CYPRESS_NPM_PACKAGE_NAME" 22 | 23 | echo "--- Cypress version" 24 | npx cypress version 25 | 26 | echo "+++ Run Cypress tests" 27 | npm run test:ci:record 28 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # testing using CircleCI orb 2 | # see https://on.cypress.io/circleci-orb 3 | 4 | # for non-orb configuration see old commit 5 | # https://github.com/cypress-io/cypress-example-kitchensink/blob/aabb10cc1bb9dee88e1bf28e0af5e9661427ee7a/circle.yml 6 | 7 | # to use CircleCI orbs need to use version >= 2.1 8 | version: 2.1 9 | orbs: 10 | # use Cypress orb from CircleCI registry 11 | cypress: cypress-io/cypress@4.1.0 12 | win: circleci/windows@5.1.0 13 | 14 | executors: 15 | mac: 16 | macos: 17 | xcode: "16.2.0" 18 | resource_class: macos.m1.medium.gen1 19 | 20 | jobs: 21 | win-test: 22 | working_directory: ~/app 23 | executor: 24 | name: win/default 25 | size: medium 26 | shell: bash.exe 27 | steps: 28 | - checkout 29 | - run: 30 | name: Install Node.js 31 | command: | 32 | nvm --version 33 | nvm install 22 34 | nvm use 22 35 | - restore_cache: 36 | key: dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} 37 | 38 | - run: npm ci 39 | - run: npm run cy:verify 40 | - run: npm run cy:info 41 | 42 | - save_cache: 43 | key: dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} 44 | paths: 45 | # could not use environment variables for some reason 46 | - C:\Users\circleci\AppData\Local\Cypress\Cache 47 | - C:\Users\circleci\AppData\Roaming\npm-cache 48 | 49 | - run: 50 | name: 'Start server' 51 | command: npm run start 52 | background: true 53 | 54 | - run: 55 | name: 'Run Cypress tests' 56 | command: npm run e2e:record -- --env circle=true 57 | no_output_timeout: '1m' 58 | - store_artifacts: 59 | path: cypress\screenshots 60 | 61 | win-test-chrome: 62 | working_directory: ~/app 63 | executor: 64 | name: win/default 65 | size: medium 66 | shell: bash.exe 67 | steps: 68 | - checkout 69 | - run: 70 | name: Install Node.js 71 | command: | 72 | nvm --version 73 | nvm install 22 74 | nvm use 22 75 | - restore_cache: 76 | key: dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} 77 | 78 | # install Chrome browser on Windows machine using Chocolatey 79 | # https://chocolatey.org/packages/GoogleChrome 80 | - run: choco install googlechrome --ignore-checksums -y 81 | - run: npm ci 82 | - run: npm run cy:verify 83 | - run: npm run cy:info 84 | 85 | - save_cache: 86 | key: dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} 87 | paths: 88 | # could not use environment variables for some reason 89 | - C:\Users\circleci\AppData\Local\Cypress\Cache 90 | - C:\Users\circleci\AppData\Roaming\npm-cache 91 | 92 | - run: 93 | name: 'Start server' 94 | command: npm run start 95 | background: true 96 | 97 | - run: 98 | name: 'Run Cypress tests' 99 | command: npm run e2e:record:chrome -- --env circle=true 100 | no_output_timeout: '1m' 101 | - store_artifacts: 102 | path: cypress\screenshots 103 | 104 | win-test-firefox: 105 | working_directory: ~/app 106 | executor: 107 | name: win/default 108 | size: medium 109 | shell: bash.exe 110 | steps: 111 | - checkout 112 | - run: 113 | name: Install Node.js 114 | command: | 115 | nvm --version 116 | nvm install 22 117 | nvm use 22 118 | - restore_cache: 119 | key: dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} 120 | 121 | # install Firefox browser on Windows machine using Chocolatey 122 | # https://chocolatey.org/packages/Firefox 123 | - run: choco install firefox -y 124 | - run: npm ci 125 | - run: npm run cy:verify 126 | - run: npm run cy:info 127 | 128 | - save_cache: 129 | key: dependencies-{{ arch }}-{{ .Branch }}-{{ checksum "package.json" }} 130 | paths: 131 | # could not use environment variables for some reason 132 | - C:\Users\circleci\AppData\Local\Cypress\Cache 133 | - C:\Users\circleci\AppData\Roaming\npm-cache 134 | 135 | - run: 136 | name: 'Start server' 137 | command: npm run start 138 | background: true 139 | 140 | - run: 141 | name: 'Run Cypress tests' 142 | command: npm run e2e:record:firefox -- --env circle=true 143 | no_output_timeout: '1m' 144 | - store_artifacts: 145 | path: cypress\screenshots 146 | 147 | mac-test: 148 | executor: mac 149 | steps: 150 | - run: 151 | name: Install Node.js 152 | command: | 153 | nvm install 22 154 | nvm use 22 155 | - cypress/install: 156 | post-install: "npm run build" 157 | # show Cypress cache folder and binary versions 158 | # to check if we are caching previous binary versions 159 | - run: npx cypress cache path 160 | - run: npx cypress cache list 161 | - run: npx cypress info 162 | - cypress/run-tests: 163 | start-command: 'npm run start' 164 | cypress-command: 'npx cypress run --record --group Mac build' 165 | 166 | linux-test: 167 | # checks out code and installs dependencies once 168 | # runs on 3 machines, load balances tests 169 | # and records on Cypress Cloud 170 | parallelism: 3 171 | executor: 172 | name: cypress/default 173 | node-version: '22.15.0' 174 | steps: 175 | - cypress/install: 176 | post-install: 'npm run build' 177 | - cypress/run-tests: 178 | start-command: 'npm run start' 179 | cypress-command: 'npx cypress run --record --parallel --group 3x-electron on CircleCI' 180 | - run: npx cypress cache path 181 | - run: npx cypress cache list 182 | - run: npx cypress info 183 | # let's print version info 184 | - run: npx cypress version 185 | - run: npx cypress version --component package 186 | - run: npx cypress version --component binary 187 | - run: npx cypress version --component electron 188 | - run: npx cypress version --component node 189 | 190 | linux-test-chrome: 191 | # runs on 2 machines using Chrome browser 192 | parallelism: 2 193 | executor: 194 | name: cypress/default 195 | node-version: '22.15.0' 196 | steps: 197 | - cypress/install: 198 | install-browsers: true 199 | - cypress/run-tests: 200 | start-command: 'npm run start' 201 | cypress-command: 'npx cypress run --browser chrome --record --parallel --group 2x-chrome on CircleCI' 202 | 203 | linux-test-firefox: 204 | # runs on 2 machines using Firefox browser 205 | parallelism: 2 206 | executor: 207 | name: cypress/default 208 | node-version: '22.15.0' 209 | steps: 210 | - cypress/install: 211 | install-browsers: true 212 | - cypress/run-tests: 213 | start-command: 'npm run start' 214 | cypress-command: 'npx cypress run --browser firefox --record --parallel --group 2x-firefox on CircleCI' 215 | 216 | release: 217 | executor: 218 | name: cypress/default 219 | node-version: '22.15.0' 220 | steps: 221 | - checkout 222 | - run: npm ci 223 | - run: npx semantic-release 224 | 225 | workflows: 226 | win-build: 227 | jobs: 228 | - win-test 229 | - win-test-chrome 230 | - win-test-firefox 231 | 232 | mac-build: 233 | jobs: 234 | - mac-test 235 | 236 | linux-build: 237 | jobs: 238 | - linux-test 239 | - linux-test-chrome 240 | - linux-test-firefox 241 | 242 | - release: 243 | filters: 244 | branches: 245 | only: 246 | - master 247 | requires: 248 | - linux-test 249 | - linux-test-chrome 250 | - linux-test-firefox 251 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.github/workflows/add-issue-to-triage-board.yml: -------------------------------------------------------------------------------- 1 | name: 'Add issue/PR to Triage Board' 2 | on: 3 | issues: 4 | types: 5 | - opened 6 | pull_request_target: 7 | types: 8 | - opened 9 | jobs: 10 | add-to-triage-project-board: 11 | uses: cypress-io/cypress/.github/workflows/triage_add_to_project.yml@develop 12 | secrets: inherit 13 | -------------------------------------------------------------------------------- /.github/workflows/chrome-docker.yml: -------------------------------------------------------------------------------- 1 | name: Chrome Docker 2 | 3 | on: [push, workflow_dispatch] 4 | 5 | jobs: 6 | # run Chrome inside a Docker container 7 | chrome: 8 | runs-on: ubuntu-24.04 9 | # https://github.com/cypress-io/cypress-docker-images 10 | container: cypress/browsers:22.15.0 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | - name: Chrome 15 | uses: cypress-io/github-action@v6 16 | timeout-minutes: 10 17 | with: 18 | build: npm run build 19 | start: npm start 20 | browser: chrome 21 | -------------------------------------------------------------------------------- /.github/workflows/chrome.yml: -------------------------------------------------------------------------------- 1 | name: Chrome 2 | 3 | on: [push, workflow_dispatch] 4 | 5 | jobs: 6 | chrome: 7 | runs-on: ubuntu-24.04 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | - name: Set up Node.js 12 | uses: actions/setup-node@v4 13 | with: 14 | node-version-file: .node-version 15 | - name: Chrome 16 | uses: cypress-io/github-action@v6 17 | timeout-minutes: 10 18 | with: 19 | build: npm run build 20 | start: npm start 21 | browser: chrome 22 | -------------------------------------------------------------------------------- /.github/workflows/parallel.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions 2 | # https://docs.github.com/en/actions/using-workflows 3 | # See also .github/workflows/using-action.yml in this repository 4 | # for an example of using the Cypress GitHub JavaScript action 5 | # https://github.com/cypress-io/github-action 6 | # 7 | name: Cypress parallel tests 8 | 9 | on: [push, workflow_dispatch] 10 | 11 | jobs: 12 | install: 13 | name: Install npm and Cypress 14 | runs-on: ubuntu-24.04 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | # install a specific version of Node using 19 | # https://github.com/actions/setup-node 20 | - name: Set up Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version-file: .node-version 24 | 25 | # just so we learn about available environment variables GitHub provides 26 | - name: Print CI env variables 27 | run: | 28 | npm i -g @bahmutov/print-env@2 29 | print-env GITHUB BUILD ACTIONS || true 30 | 31 | # Restore the previous npm modules and Cypress binary archives. 32 | # Any updated archives will be saved automatically after the entire 33 | # workflow successfully finishes. 34 | # See https://github.com/actions/cache 35 | # we use exact restore key to avoid npm module snowballing 36 | # https://glebbahmutov.com/blog/do-not-let-npm-cache-snowball/ 37 | - name: Cache central npm modules 38 | uses: actions/cache@v4 39 | with: 40 | path: ~/.npm 41 | key: ${{ runner.os }}-node-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 42 | restore-keys: | 43 | ${{ runner.os }}-node-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 44 | 45 | # we use the exact restore key to avoid Cypress binary snowballing 46 | # https://glebbahmutov.com/blog/do-not-let-cypress-cache-snowball/ 47 | - name: Cache Cypress binary 48 | uses: actions/cache@v4 49 | with: 50 | path: ~/.cache/Cypress 51 | key: cypress-${{ runner.os }}-cypress-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 52 | restore-keys: | 53 | cypress-${{ runner.os }}-cypress-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 54 | 55 | # Cache local node_modules to pass to testing jobs 56 | - name: Cache local node_modules 57 | uses: actions/cache@v4 58 | with: 59 | path: node_modules 60 | key: ${{ runner.os }}-node-modules-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 61 | restore-keys: | 62 | ${{ runner.os }}-node-modules-${{ github.ref }}- 63 | 64 | - name: install dependencies and verify Cypress 65 | env: 66 | # make sure every Cypress install prints minimal information 67 | CI: 1 68 | run: | 69 | npm ci 70 | npx cypress cache path 71 | npx cypress cache list 72 | npx cypress verify 73 | npx cypress info 74 | 75 | # Duplicate job definitions - GitHub YAML does not support 76 | # anchor definitions yet, thus we cannot put same steps into a template object yet 77 | test1: 78 | name: Cypress test 1 79 | runs-on: ubuntu-24.04 80 | needs: install 81 | steps: 82 | - uses: actions/checkout@v4 83 | 84 | # install a specific version of Node using 85 | # https://github.com/actions/setup-node 86 | - name: Set up Node.js 87 | uses: actions/setup-node@v4 88 | with: 89 | node-version-file: .node-version 90 | 91 | # Restore just local node_modules and the Cypress binary archives. 92 | - name: Cache Cypress binary 93 | uses: actions/cache@v4 94 | with: 95 | path: ~/.cache/Cypress 96 | key: cypress-${{ runner.os }}-cypress-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 97 | restore-keys: | 98 | cypress-${{ runner.os }}-cypress-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 99 | 100 | - name: Cache local node_modules 101 | uses: actions/cache@v4 102 | with: 103 | path: node_modules 104 | key: ${{ runner.os }}-node-modules-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 105 | restore-keys: | 106 | ${{ runner.os }}-node-modules-${{ github.ref }}- 107 | 108 | # check the restored Cypress binary 109 | - name: Check binary 110 | run: | 111 | npx cypress cache path 112 | npx cypress cache list 113 | 114 | # Starts local server, then runs Cypress tests and records results on Cypress Cloud 115 | - name: Cypress tests 116 | run: | 117 | npm start & 118 | npx cypress run --record --parallel --group "Parallel 2x" 119 | env: 120 | # place your secret record key at 121 | # https://github.com/cypress-io/cypress-example-kitchensink/settings/secrets 122 | CYPRESS_RECORD_KEY: ${{ secrets.dashboardRecordKey }} 123 | TERM: xterm 124 | 125 | # Save screenshots as test artifacts 126 | # https://github.com/actions/upload-artifact 127 | - uses: actions/upload-artifact@v4 128 | # there might be no screenshots created when: 129 | # - there are no test failures 130 | # so only upload screenshots if previous step has failed 131 | if: failure() 132 | with: 133 | name: screenshots 134 | path: cypress/screenshots 135 | 136 | test2: 137 | name: Cypress test 2 138 | runs-on: ubuntu-24.04 139 | needs: install 140 | steps: 141 | - uses: actions/checkout@v4 142 | 143 | # install a specific version of Node using 144 | # https://github.com/actions/setup-node 145 | - name: Set up Node.js 146 | uses: actions/setup-node@v4 147 | with: 148 | node-version-file: .node-version 149 | 150 | # Restore just local node_modules and the Cypress binary archives. 151 | - name: Cache Cypress binary 152 | uses: actions/cache@v4 153 | with: 154 | path: ~/.cache/Cypress 155 | key: cypress-${{ runner.os }}-cypress-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 156 | restore-keys: | 157 | cypress-${{ runner.os }}-cypress-${{ github.ref }}- 158 | 159 | - name: Cache local node_modules 160 | uses: actions/cache@v4 161 | with: 162 | path: node_modules 163 | key: ${{ runner.os }}-node-modules-${{ github.ref }}-${{ hashFiles('**/package-lock.json') }} 164 | restore-keys: | 165 | ${{ runner.os }}-node-modules- 166 | 167 | # check the restored Cypress binary 168 | - name: Check binary 169 | run: | 170 | npx cypress cache path 171 | npx cypress cache list 172 | 173 | # Starts local server, then runs Cypress tests and records results on Cypress Cloud 174 | - name: Cypress tests 175 | run: | 176 | npm start & 177 | npx cypress run --record --parallel --group "Parallel 2x" 178 | env: 179 | # place your secret record key at 180 | # https://github.com/cypress-io/cypress-example-kitchensink/settings/secrets 181 | CYPRESS_RECORD_KEY: ${{ secrets.dashboardRecordKey }} 182 | TERM: xterm 183 | 184 | # Save screenshots as test artifacts 185 | # https://github.com/actions/upload-artifact 186 | - uses: actions/upload-artifact@v4 187 | # there might be no screenshots created when: 188 | # - there are no test failures 189 | # so only upload screenshots if previous step has failed 190 | if: failure() 191 | with: 192 | name: screenshots 193 | path: cypress/screenshots 194 | -------------------------------------------------------------------------------- /.github/workflows/semantic-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: "Semantic Pull Request" 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | main: 12 | name: Lint Title 13 | runs-on: ubuntu-latest 14 | steps: 15 | # use a fork of the GitHub action - we cannot pull in untrusted third party actions 16 | # see https://github.com/cypress-io/cypress/pull/20091#discussion_r801799647 17 | - uses: cypress-io/action-semantic-pull-request@v4 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | with: 21 | validateSingleCommit: true 22 | -------------------------------------------------------------------------------- /.github/workflows/single.yml: -------------------------------------------------------------------------------- 1 | # GitHub Actions 2 | # https://docs.github.com/en/actions/using-workflows 3 | name: Cypress single tests 4 | 5 | on: [push, workflow_dispatch] 6 | 7 | jobs: 8 | test1: 9 | name: Cypress test 10 | runs-on: ubuntu-24.04 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | # install a specific version of Node using 15 | # https://github.com/actions/setup-node 16 | - name: Set up Node.js 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version-file: .node-version 20 | 21 | # just so we learn about available environment variables GitHub provides 22 | - name: Print env variables 23 | run: | 24 | npm i -g @bahmutov/print-env 25 | print-env GITHUB 26 | 27 | # Restore the previous npm modules and Cypress binary archives. 28 | # In case there's no previous cache the packages will be downloaded 29 | # and saved automatically after the entire workflow successfully finishes. 30 | # See https://github.com/actions/cache 31 | - name: Cache node modules 32 | uses: actions/cache@v4 33 | with: 34 | path: ~/.npm 35 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 36 | 37 | - name: Cache Cypress binary 38 | uses: actions/cache@v4 39 | with: 40 | path: ~/.cache/Cypress 41 | key: cypress-${{ runner.os }}-cypress-${{ hashFiles('**/package-lock.json') }} 42 | 43 | - name: install dependencies and verify Cypress 44 | env: 45 | # make sure every Cypress install prints minimal information 46 | CI: 1 47 | # print Cypress and OS info 48 | run: | 49 | npm ci 50 | npx cypress verify 51 | npx cypress info 52 | npx cypress version 53 | npx cypress version --component package 54 | npx cypress version --component binary 55 | npx cypress version --component electron 56 | npx cypress version --component node 57 | 58 | # Starts local server, then runs Cypress tests and records results on Cypress Cloud 59 | - name: Cypress tests 60 | run: npm run test:ci:record 61 | env: 62 | # place your secret record key at 63 | # https://github.com/cypress-io/cypress-example-kitchensink/settings/secrets 64 | CYPRESS_RECORD_KEY: ${{ secrets.dashboardRecordKey }} 65 | TERM: xterm 66 | 67 | # Save screenshots as test artifacts 68 | # https://github.com/actions/upload-artifact 69 | - uses: actions/upload-artifact@v4 70 | # there might be no screenshots created when: 71 | # - there are no test failures 72 | # so only upload screenshots if previous step has failed 73 | if: failure() 74 | with: 75 | name: screenshots 76 | path: cypress/screenshots 77 | -------------------------------------------------------------------------------- /.github/workflows/triage_closed_issue_comment.yml: -------------------------------------------------------------------------------- 1 | name: 'Handle Comment Workflow' 2 | on: 3 | issue_comment: 4 | types: 5 | - created 6 | jobs: 7 | closed-issue-comment: 8 | uses: cypress-io/cypress/.github/workflows/triage_handle_new_comments.yml@develop 9 | secrets: inherit 10 | -------------------------------------------------------------------------------- /.github/workflows/using-action.yml: -------------------------------------------------------------------------------- 1 | # Note: make sure to use the same version of cypress-io/github-action 2 | # in all jobs, otherwise the last version wins I believe 3 | name: Using Cypress GH Action 4 | 5 | on: [push, workflow_dispatch] 6 | 7 | jobs: 8 | single-run: 9 | runs-on: ubuntu-24.04 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | - name: Set up Node.js 14 | uses: actions/setup-node@v4 15 | with: 16 | node-version-file: .node-version 17 | - name: Cypress run 18 | uses: cypress-io/github-action@v6 19 | timeout-minutes: 10 20 | with: 21 | build: npm run build 22 | start: npm start 23 | 24 | parallel-runs: 25 | name: Parallel 4x 26 | runs-on: ubuntu-24.04 27 | strategy: 28 | # when one test fails, DO NOT cancel the other 29 | # containers, because this will kill Cypress processes 30 | # leaving the Cypress Cloud dashboard hanging ... 31 | # https://github.com/cypress-io/github-action/issues/48 32 | fail-fast: false 33 | matrix: 34 | # Run 4 copies of the current job in parallel. 35 | # The name of the array (containers) and 36 | # the actual items in the array (1, 2, 3, 4) do not matter. 37 | # Based on the array, GitHub Actions will create 38 | # 4 independently running parallel jobs 39 | # (see https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs). 40 | # Cypress Cloud load-balances the Cypress tests across the GitHub Actions jobs. 41 | containers: [1, 2, 3, 4] 42 | steps: 43 | - name: Checkout 44 | uses: actions/checkout@v4 45 | - name: Set up Node.js 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version-file: .node-version 49 | # because of "record" and "parallel" parameters 50 | # these containers will load balance all found tests among themselves 51 | - name: run tests 52 | uses: cypress-io/github-action@v6 53 | timeout-minutes: 5 54 | with: 55 | record: true 56 | parallel: true 57 | group: GH Action parallel 58 | start: npm start 59 | env: 60 | # pass the Dashboard record key as an environment variable 61 | CYPRESS_RECORD_KEY: ${{ secrets.dashboardRecordKey }} 62 | 63 | parallel-runs-across-platforms: 64 | name: every OS 65 | strategy: 66 | # when one test fails, DO NOT cancel the other 67 | # containers, because this will kill Cypress processes 68 | # leaving the Cypress Cloud dashboard hanging ... 69 | # https://github.com/cypress-io/github-action/issues/48 70 | fail-fast: false 71 | matrix: 72 | # run 2 copies of the current job in parallel 73 | # and they will load balance all specs 74 | os: ['ubuntu-24.04', 'windows-latest', 'macos-latest'] 75 | containers: [1, 2] 76 | runs-on: ${{ matrix.os }} 77 | steps: 78 | - name: Checkout 79 | uses: actions/checkout@v4 80 | - name: Set up Node.js 81 | uses: actions/setup-node@v4 82 | with: 83 | node-version-file: .node-version 84 | # because of "record" and "parallel" parameters 85 | # these containers will load balance all found tests among themselves 86 | - name: run tests 87 | uses: cypress-io/github-action@v6 88 | timeout-minutes: 10 89 | with: 90 | record: true 91 | parallel: true 92 | group: Parallel 2x on ${{ matrix.os }} 93 | # on Mac and Linux we can use "npm start" 94 | start: npm start 95 | # but for this particular project on Windows we need a different start command 96 | start-windows: npm run start 97 | env: 98 | # pass the Cypress Cloud (dashboard) record key as an environment variable 99 | CYPRESS_RECORD_KEY: ${{ secrets.dashboardRecordKey }} 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | *.aes 4 | cypress/downloads 5 | cypress/screenshots 6 | cypress/videos 7 | cypress/logs 8 | cypress/fixtures/profile.json 9 | cypress/fixtures/users.json 10 | .history 11 | .vscode 12 | env 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # first, install Cypress, then run all tests (in parallel) 2 | stages: 3 | - build 4 | - test 5 | 6 | # to cache both npm modules and Cypress binary we use environment variables 7 | # to point at the folders we can list as paths in "cache" job settings 8 | variables: 9 | npm_config_cache: "$CI_PROJECT_DIR/.npm" 10 | CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/Cypress" 11 | 12 | # cache using branch name 13 | # https://gitlab.com/help/ci/caching/index.md 14 | cache: 15 | key: ${CI_COMMIT_REF_SLUG} 16 | paths: 17 | - .npm 18 | - cache/Cypress 19 | - node_modules 20 | 21 | # this job installs npm dependencies and Cypress 22 | install: 23 | image: cypress/base:22.15.0 24 | stage: build 25 | 26 | script: 27 | - npm ci 28 | # check Cypress binary path and cached versions 29 | # useful to make sure we are not carrying around old versions 30 | - npx cypress cache path 31 | - npx cypress cache list 32 | - npx print-env CI 33 | - npm run cy:verify 34 | - npm run cy:info 35 | 36 | # all jobs that actually run tests can use the same definition 37 | .job_template: 38 | image: cypress/base:22.15.0 39 | stage: test 40 | script: 41 | # print CI environment variables for reference 42 | - npx print-env CI 43 | # start the server in the background 44 | - npm run start & 45 | # run Cypress test in load balancing mode 46 | - npm run e2e:record -- --parallel --group "electrons on GitLab CI" 47 | artifacts: 48 | when: always 49 | paths: 50 | - cypress/screenshots/**/*.png 51 | expire_in: 1 day 52 | 53 | # actual job definitions 54 | # all steps are the same, they come from the template above 55 | electrons-1: 56 | extends: .job_template 57 | electrons-2: 58 | extends: .job_template 59 | electrons-3: 60 | extends: .job_template 61 | electrons-4: 62 | extends: .job_template 63 | electrons-5: 64 | extends: .job_template 65 | -------------------------------------------------------------------------------- /.husky/install.mjs: -------------------------------------------------------------------------------- 1 | // install.mjs 2 | // 3 | // See "CI server and Docker" in 4 | // https://typicode.github.io/husky/how-to.html#ci-server-and-docker 5 | // to avoid permissions issue with npm ci in a Docker container 6 | // 7 | const isCi = process.env.CI !== undefined 8 | if (!isCi) { 9 | const husky = (await import('husky')).default 10 | console.log(husky()) 11 | } 12 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run lint 2 | npm run lint:yaml 3 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | save-exact=true 2 | package-lock=true 3 | -------------------------------------------------------------------------------- /.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "commitMessage": "release %s", 3 | "tagAnnotation": "release %s", 4 | "github": { 5 | "release": false, 6 | "releaseName": "release %s" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | # copied from https://docs.semaphoreci.com/article/50-pipeline-yaml 2 | # NodeJS and JavaScript docs 3 | # https://docs.semaphoreci.com/article/82-language-javascript-and-nodejs 4 | 5 | # Semaphore v2 API starts with version 1.0 :) 6 | version: v1.0 7 | name: Cypress example Kitchensink 8 | 9 | agent: 10 | # Dummy agent - the real agent is under the "task" 11 | # but seems Semaphore requires top level one 12 | machine: 13 | type: e1-standard-2 14 | 15 | blocks: 16 | - name: Mac E2E tests 17 | # disable Mac tests with built-in Electron 18 | # until the following issue is resolved 19 | # https://github.com/cypress-io/cypress-example-kitchensink/issues/269 20 | # probably by upgrading Electron 21 | # https://github.com/renderedtext/when#skip-block-exection 22 | # still broken in Cypress v3.7.0 23 | skip: 24 | when: "true" 25 | 26 | task: 27 | agent: 28 | machine: 29 | type: a1-standard-4 30 | 31 | prologue: 32 | commands: 33 | - checkout 34 | - node --version 35 | - npm --version 36 | - npm ci 37 | # verify the Cypress test binary so its check status is cached 38 | - npm run cy:verify 39 | - npx cypress cache path 40 | - npx cypress cache list 41 | 42 | # prints SEMAPHORE_* environment variables 43 | - npm run print-env -- SEMAPHORE 44 | jobs: 45 | - name: Cypress E2E 46 | commands: 47 | # prints SEMAPHORE_* environment variables 48 | - npm run print-env -- SEMAPHORE 49 | - npm run e2e 50 | 51 | - name: Linux E2E tests 52 | task: 53 | agent: 54 | machine: 55 | type: e1-standard-2 56 | 57 | # see https://docs.semaphoreci.com/article/66-environment-variables-and-secrets 58 | secrets: 59 | # we have created new secrets object at https://cypress-io.semaphoreci.com/secrets/new 60 | # for this organization. In this job we can now access CYPRESS_RECORD_KEY 61 | - name: dashboard 62 | 63 | env_vars: 64 | # skip Puppeteer download, only needed for Netlify build 65 | - name: PUPPETEER_SKIP_DOWNLOAD 66 | value: "1" 67 | 68 | # common commands that should be done before each E2E test command 69 | prologue: 70 | commands: 71 | - nvm install 22 72 | - npm install -g npm 73 | - checkout 74 | 75 | # restore previously cached files if any 76 | # re-install dependencies if package.json or this semaphore YML file changes 77 | - cache restore npm-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) 78 | - cache restore cypress-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) 79 | 80 | - npm ci 81 | # verify the Cypress test binary so its check status is cached 82 | - npm run cy:verify 83 | # Cache npm dependencies and Cypress binary 84 | - cache store npm-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) ~/.npm 85 | - cache store cypress-$SEMAPHORE_GIT_BRANCH-$(checksum package-lock.json)-$(checksum .semaphore/semaphore.yml) ~/.cache/Cypress 86 | 87 | # prints SEMAPHORE_* environment variables 88 | - npm run print-env -- SEMAPHORE 89 | # finally, build the web application and run end-to-end tests 90 | - npm run build 91 | # start the web application server in the background 92 | - npm run start & 93 | 94 | jobs: 95 | # all "prologue" commands have finished by now 96 | # and we can define a single job to execute in parallel on N machines 97 | # Cypress recognizes Semaphore environment variables 98 | # in order to link these separate steps into a single run. 99 | # see Cypress https://on.cypress.io/parallelization 100 | - name: Cypress E2E 101 | parallelism: 3 102 | commands: 103 | - npx cypress run --record --parallel --group "Semaphore 3x" 104 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | env: 3 | # skip Puppeteer download, only needed for Netlify build 4 | - PUPPETEER_SKIP_DOWNLOAD=1 5 | 6 | node_js: 7 | - 22 8 | 9 | cache: 10 | # cache both npm modules and Cypress binary 11 | directories: 12 | - ~/.npm 13 | - ~/.cache 14 | override: 15 | - npm ci 16 | - npm run cy:verify 17 | - npm run cy:info 18 | 19 | defaults: &defaults 20 | script: 21 | # ## print all Travis environment variables for debugging 22 | - npx print-env TRAVIS 23 | - npm run start & 24 | - npm run cy:run -- --record --parallel --group $STAGE_NAME 25 | # after all tests finish running we need 26 | # to kill all background jobs (like "npm start &") 27 | - kill $(jobs -p) || true 28 | 29 | jobs: 30 | include: 31 | # we have multiple jobs to execute using just a single stage 32 | # but we can pass group name via environment variable to Cypress test runner 33 | - stage: test 34 | env: 35 | - STAGE_NAME="1x-electron on Travis CI" 36 | <<: *defaults 37 | # run tests in parallel by including several test jobs with same name variable 38 | - stage: test 39 | env: 40 | - STAGE_NAME="4x-electron on Travis CI" 41 | <<: *defaults 42 | - stage: test 43 | env: 44 | - STAGE_NAME="4x-electron on Travis CI" 45 | <<: *defaults 46 | - stage: test 47 | env: 48 | - STAGE_NAME="4x-electron on Travis CI" 49 | <<: *defaults 50 | - stage: test 51 | env: 52 | - STAGE_NAME="4x-electron on Travis CI" 53 | <<: *defaults 54 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "standard.enable": false, 3 | "eslint.enable": true, 4 | "typescript.surveys.enabled": false, 5 | "editor.formatOnSave": false 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repo contains the source code that is required to build [https://example.cypress.io](https://example.cypress.io). 4 | 5 | This repo is required by the [example](https://github.com/cypress-io/cypress/tree/develop/packages/example) package of Cypress main code base. 6 | 7 | ## Deployment 8 | 9 | 1. After landing commits with features or fixes, new versions of this npm package are published automatically using `semantic-release`. 10 | 11 | 2. Now the [example](https://github.com/cypress-io/cypress/tree/develop/packages/example) package in Cypress' main code base must be updated. [Follow these instructions](https://github.com/cypress-io/cypress/blob/develop/packages/example/README.md#updating-the-example-app) to update the `example` app. 12 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This Docker file is for building this project on Codeship Pro 2 | # https://documentation.codeship.com/pro/languages-frameworks/nodejs/ 3 | 4 | # use Cypress provided image with all dependencies included 5 | FROM cypress/base:22.15.0 6 | RUN node --version 7 | RUN npm --version 8 | WORKDIR /home/node/app 9 | # copy our test application 10 | COPY package.json package-lock.json ./ 11 | COPY app ./app 12 | COPY serve.json ./ 13 | COPY scripts ./scripts 14 | # copy Cypress tests 15 | COPY cypress.config.js cypress ./ 16 | COPY cypress ./cypress 17 | 18 | # avoid many lines of progress bars during install 19 | # https://github.com/cypress-io/cypress/issues/1243 20 | ENV CI=1 21 | 22 | # install npm dependencies and Cypress binary 23 | RUN npm ci 24 | # check if the binary was installed successfully 25 | RUN npx cypress verify 26 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | // Example Jenkins pipeline with Cypress end-to-end tests running in parallel on 2 workers 2 | // Pipeline syntax from https://jenkins.io/doc/book/pipeline/ 3 | 4 | // Setup: 5 | // before starting Jenkins, I have created several volumes to cache 6 | // Jenkins configuration, npm modules and Cypress binary 7 | 8 | // docker volume create jenkins-data 9 | // docker volume create npm-cache 10 | // docker volume create cypress-cache 11 | 12 | // Start Jenkins command line by line: 13 | // - run as "root" user (insecure, contact your admin to configure user and groups!) 14 | // - run Docker in disconnected mode 15 | // - name running container "blue-ocean" 16 | // - map port 8080 with Jenkins UI 17 | // - map volumes for Jenkins data, npm and Cypress caches 18 | // - pass Docker socket which allows Jenkins to start worker containers 19 | // - download and execute the latest BlueOcean Docker image 20 | 21 | // docker run \ 22 | // -u root \ 23 | // -d \ 24 | // --name blue-ocean \ 25 | // -p 8080:8080 \ 26 | // -v jenkins-data:/var/jenkins_home \ 27 | // -v npm-cache:/root/.npm \ 28 | // -v cypress-cache:/root/.cache \ 29 | // -v /var/run/docker.sock:/var/run/docker.sock \ 30 | // jenkinsci/blueocean:latest 31 | 32 | // If you start for the very first time, inspect the logs from the running container 33 | // to see Administrator password - you will need it to configure Jenkins via localhost:8080 UI 34 | // docker logs blue-ocean 35 | 36 | pipeline { 37 | agent { 38 | // this image provides everything needed to run Cypress 39 | docker { 40 | image 'cypress/base:22.15.0' 41 | } 42 | } 43 | 44 | stages { 45 | // first stage installs node dependencies and Cypress binary 46 | stage('build') { 47 | steps { 48 | // there a few default environment variables on Jenkins 49 | // on local Jenkins machine (assuming port 8080) see 50 | // http://localhost:8080/pipeline-syntax/globals#env 51 | echo "Running build ${env.BUILD_ID} on ${env.JENKINS_URL}" 52 | sh 'npm ci' 53 | sh 'npm run cy:verify' 54 | } 55 | } 56 | 57 | stage('start local server') { 58 | steps { 59 | // start local server in the background 60 | // we will shut it down in "post" command block 61 | sh 'nohup npm run start &' 62 | } 63 | } 64 | 65 | // this stage runs end-to-end tests, and each agent uses the workspace 66 | // from the previous stage 67 | stage('cypress parallel tests') { 68 | environment { 69 | // we will be recording test results on Cypress Cloud 70 | // to record we need to set an environment variable 71 | // we can load the record key variable from credentials store 72 | // see https://jenkins.io/doc/book/using/using-credentials/ 73 | CYPRESS_RECORD_KEY = credentials('cypress-example-kitchensink-record-key') 74 | // because parallel steps share the workspace they might race to delete 75 | // screenshots folders. Tell Cypress not to delete these folders 76 | CYPRESS_trashAssetsBeforeRuns = 'false' 77 | } 78 | 79 | // https://jenkins.io/doc/book/pipeline/syntax/#parallel 80 | parallel { 81 | // start several test jobs in parallel, and they all 82 | // will use Cypress Cloud to load balance any found spec files 83 | stage('tester A') { 84 | steps { 85 | echo "Running build ${env.BUILD_ID}" 86 | sh "npm run e2e:record:parallel" 87 | } 88 | } 89 | 90 | // second tester runs the same command 91 | stage('tester B') { 92 | steps { 93 | echo "Running build ${env.BUILD_ID}" 94 | sh "npm run e2e:record:parallel" 95 | } 96 | } 97 | } 98 | 99 | } 100 | } 101 | 102 | post { 103 | // shutdown the server running in the background 104 | always { 105 | echo 'Stopping local server' 106 | sh 'pkill -f http-server' 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Cypress.io, Inc. 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /amplify.yml: -------------------------------------------------------------------------------- 1 | version: 0.1 2 | frontend: 3 | phases: 4 | preBuild: 5 | commands: 6 | - npm install 7 | build: 8 | commands: 9 | - npm run build 10 | artifacts: 11 | baseDirectory: app 12 | files: 13 | - "**/*" 14 | cache: 15 | paths: 16 | - node_modules/**/* 17 | test: 18 | artifacts: 19 | baseDirectory: cypress 20 | configFilePath: "**/mochawesome.json" 21 | files: 22 | - "**/*.png" 23 | - "**/*.mp4" 24 | phases: 25 | preTest: 26 | commands: 27 | - npm install 28 | - npm install wait-on 29 | - npm install mocha mochawesome mochawesome-merge mochawesome-report-generator 30 | - "npm start & npx wait-on http://127.0.0.1:8080" 31 | test: 32 | commands: 33 | - 'npx cypress run --reporter mochawesome --reporter-options "reportDir=cypress/report/mochawesome-report,overwrite=false,html=false,json=true,timestamp=mmddyyyy_HHMMss"' 34 | postTest: 35 | commands: 36 | - npx mochawesome-merge@4 cypress/report/mochawesome-report/*.json > cypress/report/mochawesome.json 37 | -------------------------------------------------------------------------------- /app/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 0; 3 | font-family: 'Fira Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 4 | } 5 | 6 | h4 { 7 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 8 | font-size: 22px; 9 | } 10 | 11 | code { 12 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 13 | } 14 | 15 | pre { 16 | font-size: 13px; 17 | border: 0; 18 | padding: 16px; 19 | background: #f7f7f7; 20 | border-radius: 3px; 21 | box-shadow: 0 0 0 0 #fff; 22 | word-break: normal; 23 | word-wrap: normal; 24 | } 25 | 26 | table { 27 | background-color: #fff; 28 | } 29 | 30 | pre code { 31 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 32 | padding: 0; 33 | } 34 | 35 | canvas { 36 | background-color: #fff; 37 | border: 1px solid #ddd; 38 | cursor: pointer; 39 | } 40 | 41 | .well { 42 | margin-top: 75px; 43 | margin-bottom: 0; 44 | border: 0; 45 | box-shadow: 0 0 0 #fff; 46 | -webkit-box-shadow: 0 0 0 #fff; 47 | } 48 | 49 | /* Just like well, but for second row, with just a single line of text */ 50 | .well-1-line { 51 | margin-top: 2em; 52 | } 53 | 54 | .placeholder { 55 | width: 100px; 56 | height: 100px; 57 | background: #69f; 58 | } 59 | 60 | /*---------nav----------*/ 61 | 62 | .navbar { 63 | border-radius: 0; 64 | margin-bottom: 0; 65 | } 66 | 67 | .navbar-inverse .navbar-brand { 68 | color: #fff; 69 | } 70 | 71 | /*---------banner----------*/ 72 | 73 | .banner { 74 | background-color: #00bf88; 75 | color: #fff; 76 | padding: 25px 0 35px; 77 | } 78 | 79 | .banner p, 80 | .banner-alt p { 81 | font-size: 18px; 82 | } 83 | 84 | .banner a { 85 | color: #fff; 86 | border-bottom: 1px dashed #fff; 87 | } 88 | 89 | .banner-alt { 90 | background-color: #eef1f4; 91 | color: #6b707c; 92 | padding: 25px 0 35px; 93 | } 94 | 95 | .banner-alt h2 { 96 | color: #555; 97 | } 98 | 99 | .home-list { 100 | list-style: none; 101 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 102 | margin: 35px 0; 103 | padding-left: 0; 104 | line-height: 28px; 105 | } 106 | 107 | li.header { 108 | font-weight: bold; 109 | list-style: none; 110 | } 111 | 112 | .home-list > li { 113 | margin-top: 25px; 114 | padding-bottom: 20px; 115 | border-bottom: 1px solid #ddd; 116 | font-size: 20px; 117 | font-weight: bold; 118 | } 119 | 120 | .home-list > li:last-child { 121 | border-bottom: 0; 122 | } 123 | 124 | .home-list > li > ul { 125 | list-style: none; 126 | padding-left: 0px; 127 | } 128 | 129 | .home-list > li > ul > li { 130 | font-size: 16px; 131 | color: #666; 132 | } 133 | 134 | .home-list > li > ul > li > a { 135 | font-weight: normal; 136 | } 137 | 138 | .content-container { 139 | margin-top: 35px; 140 | } 141 | 142 | #query-form { 143 | border: 1px solid #ddd; 144 | padding: 10px; 145 | } 146 | 147 | .action-focus.focus { 148 | border: 5px solid orange; 149 | } 150 | 151 | .action-blur.error { 152 | border: 5px solid red; 153 | } 154 | 155 | .action-div, .rightclick-action-div { 156 | cursor: pointer; 157 | } 158 | 159 | .action-opacity { 160 | position: relative; 161 | margin-top: 50px; 162 | } 163 | 164 | .action-opacity .opacity-cover { 165 | text-align: center; 166 | line-height: 50px; 167 | color: #333; 168 | font-weight: bold; 169 | font-size: 25px; 170 | position: absolute; 171 | top: -5px; 172 | left: -5px; 173 | width: 235px; 174 | height: 60px; 175 | background-color: red; 176 | opacity: 0.7; 177 | box-shadow: 0 2px 7px #000; 178 | } 179 | 180 | .utlity-jquery li.active { 181 | background-color: #0068a2; 182 | color: white; 183 | } 184 | 185 | .network-btn, 186 | .network-post, 187 | .network-put { 188 | margin-bottom: 20px; 189 | } 190 | 191 | #scroll-horizontal, 192 | #scroll-vertical, 193 | #scroll-both, 194 | #scrollable-horizontal, 195 | #scrollable-vertical, 196 | #scrollable-both { 197 | background-color: #ddd; 198 | border: 1px solid #777; 199 | border-radius: 4px; 200 | margin-bottom: 15px; 201 | } 202 | 203 | #scrollable-horizontal ul, 204 | #scrollable-vertical ul, 205 | #scrollable-both ul { 206 | padding: 0; 207 | overflow: auto; 208 | } 209 | 210 | #scrollable-horizontal ul > li, 211 | #scrollable-vertical ul > li, 212 | #scrollable-both ul > li { 213 | list-style: none; 214 | margin: 20px; 215 | float: left; 216 | } 217 | 218 | #scrollable-horizontal ul > li { 219 | display: inline-block; 220 | } 221 | 222 | ::-webkit-scrollbar { 223 | -webkit-appearance: none; 224 | width: 12px; 225 | height: 12px; 226 | } 227 | 228 | ::-webkit-scrollbar-track { 229 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); 230 | border-radius: 4px; 231 | } 232 | 233 | ::-webkit-scrollbar-thumb { 234 | border-radius: 4px; 235 | -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.5); 236 | } 237 | 238 | /* 239 | 240 | code highlight styling 241 | 242 | */ 243 | 244 | .hljs { 245 | display: block; 246 | overflow-x: auto; 247 | color: #333; 248 | background: #f7f7f7; 249 | } 250 | 251 | .hljs-comment, 252 | .hljs-quote { 253 | color: #999; 254 | } 255 | 256 | .hljs-keyword, 257 | .hljs-selector-tag, 258 | .hljs-subst { 259 | color: #1d75b3; 260 | font-weight: normal; 261 | } 262 | 263 | .hljs-number, 264 | .hljs-literal, 265 | .hljs-variable, 266 | .hljs-template-variable, 267 | .hljs-tag .hljs-attr { 268 | color: #0086b3; 269 | font-weight: normal; 270 | } 271 | 272 | .hljs-string, 273 | .hljs-doctag { 274 | color: #183691; 275 | } 276 | 277 | .hljs-title, 278 | .hljs-section, 279 | .hljs-selector-id { 280 | color: #900; 281 | font-weight: normal; 282 | } 283 | 284 | .hljs-subst { 285 | font-weight: normal; 286 | } 287 | 288 | .hljs-type, 289 | .hljs-class .hljs-title { 290 | color: #458; 291 | font-weight: normal; 292 | } 293 | 294 | .hljs-tag, 295 | .hljs-name, 296 | .hljs-attribute { 297 | color: #000080; 298 | font-weight: normal; 299 | } 300 | 301 | .hljs-regexp, 302 | .hljs-link { 303 | color: #009926; 304 | } 305 | 306 | .hljs-symbol, 307 | .hljs-bullet { 308 | color: #990073; 309 | } 310 | 311 | .hljs-built_in, 312 | .hljs-builtin-name { 313 | color: #0086b3; 314 | } 315 | 316 | .hljs-meta { 317 | color: #999; 318 | font-weight: normal; 319 | } 320 | 321 | .hljs-deletion { 322 | background: #fdd; 323 | } 324 | 325 | .hljs-addition { 326 | background: #dfd; 327 | } 328 | 329 | .hljs-emphasis { 330 | font-style: normal; 331 | } 332 | 333 | .hljs-strong { 334 | font-weight: normal; 335 | } 336 | -------------------------------------------------------------------------------- /app/assets/css/vendor/fira.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Fira Sans'; 3 | src: url('../../fonts/eot/FiraSans-Regular.eot'); 4 | src: local('Fira Sans Regular'), 5 | url('../../fonts/eot/FiraSans-Regular.eot') format('embedded-opentype'), 6 | url('../../fonts/woff/FiraSans-Regular.woff') format('woff'), 7 | url('../../fonts/ttf/FiraSans-Regular.ttf') format('truetype'); 8 | font-weight: 400; 9 | font-style: normal; 10 | } 11 | 12 | @font-face{ 13 | font-family: 'Fira Sans'; 14 | src: url('../../fonts/eot/FiraSans-Medium.eot'); 15 | src: local('Fira Sans Medium'), 16 | url('../../fonts/eot/FiraSans-Medium.eot') format('embedded-opentype'), 17 | url('../../fonts/woff/FiraSans-Medium.woff') format('woff'), 18 | url('../../fonts/ttf/FiraSans-Medium.ttf') format('truetype'); 19 | font-weight: 500; 20 | font-style: normal; 21 | } -------------------------------------------------------------------------------- /app/assets/fonts/eot/FiraSans-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/fonts/eot/FiraSans-Medium.eot -------------------------------------------------------------------------------- /app/assets/fonts/eot/FiraSans-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/fonts/eot/FiraSans-Regular.eot -------------------------------------------------------------------------------- /app/assets/fonts/ttf/FiraSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/fonts/ttf/FiraSans-Medium.ttf -------------------------------------------------------------------------------- /app/assets/fonts/ttf/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/fonts/ttf/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /app/assets/fonts/woff/FiraSans-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/fonts/woff/FiraSans-Medium.woff -------------------------------------------------------------------------------- /app/assets/fonts/woff/FiraSans-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/fonts/woff/FiraSans-Regular.woff -------------------------------------------------------------------------------- /app/assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/img/favicon.ico -------------------------------------------------------------------------------- /app/assets/img/javascript-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cypress-io/cypress-example-kitchensink/29a02fbbbcb89eb0c75e4dfaaad32b99e8c8dddd/app/assets/img/javascript-logo.png -------------------------------------------------------------------------------- /app/assets/js/scripts.js: -------------------------------------------------------------------------------- 1 | /* global hljs, $ */ 2 | 3 | // initialize highlight.js for JavaScript code highlighting 4 | hljs.initHighlightingOnLoad() 5 | 6 | $(() => { 7 | // initialize Bootstrap popovers 8 | $('[data-toggle="popover"]').popover() 9 | 10 | // begin: draw dots on canvas on mouse click --- 11 | let canvas = document.getElementById('action-canvas') 12 | 13 | let context 14 | 15 | context = typeof canvas !== 'undefined' && canvas !== null ? canvas.getContext('2d') : 0 16 | 17 | $('#action-canvas').on('click', (e) => { 18 | draw(e) 19 | }) 20 | 21 | function draw (e) { 22 | let pos = getMousePos(canvas, e) 23 | let posx = pos.x 24 | let posy = pos.y 25 | 26 | context.fillStyle = 'red' 27 | context.beginPath() 28 | context.arc(posx, posy, 5, 0, 2 * Math.PI) 29 | context.fill() 30 | } 31 | 32 | function getMousePos (canvas, evt) { 33 | let rect = canvas.getBoundingClientRect() 34 | 35 | return { 36 | x: evt.clientX - rect.left, 37 | y: evt.clientY - rect.top, 38 | } 39 | } 40 | // end ----------------------------------------- 41 | 42 | // listen to dblclick to demonstrate logic on double click command 43 | $('.action-div').on('dblclick', (e) => { 44 | $('.action-input-hidden').removeClass('hidden').focus() 45 | $(e.currentTarget).addClass('hidden') 46 | }) 47 | 48 | // listen to contextmenu to demonstrate logic on right click command 49 | $('.rightclick-action-div').on('contextmenu', (e) => { 50 | $('.rightclick-action-input-hidden').removeClass('hidden').focus() 51 | $(e.currentTarget).addClass('hidden') 52 | }) 53 | 54 | // listen to focus to demonstrate logic on focus command 55 | $('.action-focus').on('focus', (e) => { 56 | $(e.currentTarget).addClass('focus') 57 | $(e.currentTarget).prev().css('color', 'orange') 58 | }) 59 | 60 | // listen to blur to demonstrate logic on blur command 61 | $('.action-blur').on('blur', (e) => { 62 | $(e.currentTarget).addClass('error') 63 | $(e.currentTarget).prev().css('color', 'red') 64 | }) 65 | 66 | // listen to submit to demonstrate logic on submit command 67 | $('.action-form').on('submit', (e) => { 68 | e.preventDefault() 69 | 70 | $('
Your form has been submitted!
') 71 | .insertAfter(e.currentTarget) 72 | .css('color', '#20B520') 73 | }) 74 | 75 | // hide this div so we can invoke show later 76 | $('.connectors-div').hide() 77 | 78 | // listen to click on misc-table 79 | $('.misc-table tr').on('click', (e) => { 80 | $(e.currentTarget).addClass('info') 81 | }) 82 | 83 | // listen to click on button in .as-table 84 | $('.as-table .btn').on('click', (e) => { 85 | e.preventDefault() 86 | $(e.currentTarget).addClass('btn-success').text('Changed') 87 | }) 88 | 89 | // listen to input range for trigger command 90 | $('.trigger-input-range').on('change', (e) => { 91 | const $range = $(e.target) 92 | 93 | $range.next('p').text($range.val()) 94 | }) 95 | 96 | // begin: Handle our route listeners ------------- 97 | 98 | $('.network-btn').on('click', (e) => { 99 | e.preventDefault() 100 | getComment(e) 101 | }) 102 | 103 | $('.network-post').on('click', (e) => { 104 | e.preventDefault() 105 | postComment(e) 106 | }) 107 | 108 | $('.network-put').on('click', (e) => { 109 | e.preventDefault() 110 | putComment(e) 111 | }) 112 | 113 | $('.fixture-btn').on('click', (e) => { 114 | e.preventDefault() 115 | getComment(e) 116 | }) 117 | // end ----------------------------------------- 118 | 119 | // begin: Handle our route logic ------------- 120 | // we fetch all data from this REST json backend 121 | const root = 'https://jsonplaceholder.cypress.io' 122 | 123 | function getComment () { 124 | $.ajax({ 125 | url: `${root}/comments/1`, 126 | method: 'GET', 127 | }).then((data) => { 128 | $('.network-comment').text(data.body) 129 | }) 130 | } 131 | 132 | function postComment () { 133 | $.ajax({ 134 | url: `${root}/comments`, 135 | method: 'POST', 136 | data: { 137 | name: 'Using POST in cy.intercept()', 138 | email: 'hello@cypress.io', 139 | body: 'You can change the method used for cy.intercept() to be GET, POST, PUT, PATCH, or DELETE', 140 | }, 141 | }).then(() => { 142 | $('.network-post-comment').text('POST successful!') 143 | }) 144 | } 145 | 146 | function putComment () { 147 | $.ajax({ 148 | url: `${root}/comments/1`, 149 | method: 'PUT', 150 | data: { 151 | name: 'Using PUT in cy.intercept()', 152 | email: 'hello@cypress.io', 153 | body: 'You can change the method used for cy.intercept() to be GET, POST, PUT, PATCH, or DELETE', 154 | }, 155 | statusCode: { 156 | 404 (data) { 157 | $('.network-put-comment').text(data.responseJSON.error) 158 | }, 159 | }, 160 | }) 161 | } 162 | // end ----------------------------------------- 163 | 164 | $('.ls-btn').on('click', (e) => { 165 | e.preventDefault() 166 | populateStorage(e) 167 | }) 168 | 169 | // populate localStorage and sessionStorage to demonstrate cy.clearLocalStorage() 170 | function populateStorage () { 171 | localStorage.setItem('prop1', 'red') 172 | localStorage.setItem('prop2', 'blue') 173 | localStorage.setItem('prop3', 'magenta') 174 | 175 | sessionStorage.setItem('prop4', 'cyan') 176 | sessionStorage.setItem('prop5', 'yellow') 177 | sessionStorage.setItem('prop6', 'black') 178 | } 179 | 180 | // setting a cookie 181 | $('.set-a-cookie').on('click', (e) => { 182 | e.preventDefault() 183 | setCookies(e) 184 | }) 185 | 186 | // populate local cookie to demonstrate cy.clearCookies() 187 | function setCookies () { 188 | document.cookie = 'token=123ABC' 189 | } 190 | 191 | $('.utility-jquery li').on('click', (e) => { 192 | let $li = $(e.currentTarget) 193 | 194 | $li.addClass('active') 195 | }) 196 | 197 | $('#clock-div').on('click', (e) => { 198 | let $div = $(e.currentTarget) 199 | 200 | // seconds from the unix epoch 201 | $div.text(new Date().getTime() / 1000) 202 | }) 203 | 204 | $('#tick-div').on('click', (e) => { 205 | let $div = $(e.currentTarget) 206 | 207 | // seconds from the unix epoch 208 | $div.text(new Date().getTime() / 1000) 209 | }) 210 | }) 211 | -------------------------------------------------------------------------------- /app/assets/js/todo/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Forked from TodoMVC 3 | * https://todomvc.com 4 | * 5 | * MIT License © Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk 6 | */ 7 | 8 | /*global app, $on */ 9 | (function () { 10 | 'use strict' 11 | 12 | /** 13 | * Sets up a brand new Todo list. 14 | * 15 | * @param {string} name The name of your new to do list. 16 | */ 17 | function Todo (name) { 18 | this.storage = new app.Store(name) 19 | this.model = new app.Model(this.storage) 20 | this.template = new app.Template() 21 | this.view = new app.View(this.template) 22 | this.controller = new app.Controller(this.model, this.view) 23 | } 24 | 25 | let todo = new Todo('todos-vanillajs') 26 | 27 | function init () { 28 | setView() 29 | 30 | // initialize with 2 items 31 | todo.storage.findAll((data) => { 32 | if (!data.length) { 33 | todo.controller.addItem('Pay electric bill') 34 | todo.controller.addItem('Walk the dog') 35 | } 36 | }) 37 | } 38 | 39 | function setView () { 40 | todo.controller.setView(document.location.hash) 41 | } 42 | $on(window, 'load', init) 43 | $on(window, 'hashchange', setView) 44 | })() 45 | -------------------------------------------------------------------------------- /app/assets/js/todo/helpers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Forked from TodoMVC 3 | * https://todomvc.com 4 | * 5 | * MIT License © Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk 6 | */ 7 | 8 | (function (window) { 9 | 'use strict' 10 | 11 | // Get element(s) by CSS selector: 12 | window.qs = function (selector, scope) { 13 | return (scope || document).querySelector(selector) 14 | } 15 | 16 | window.qsa = function (selector, scope) { 17 | return (scope || document).querySelectorAll(selector) 18 | } 19 | 20 | // addEventListener wrapper: 21 | window.$on = function (target, type, callback, useCapture) { 22 | target.addEventListener(type, callback, !!useCapture) 23 | } 24 | 25 | // Attach a handler to event for all elements that match the selector, 26 | // now or in the future, based on a root element 27 | window.$delegate = function (target, selector, type, handler) { 28 | function dispatchEvent (event) { 29 | let targetElement = event.target 30 | let potentialElements = window.qsa(selector, target) 31 | let hasMatch = Array.prototype.indexOf.call(potentialElements, targetElement) >= 0 32 | 33 | if (hasMatch) { 34 | handler.call(targetElement, event) 35 | } 36 | } 37 | 38 | // https://developer.mozilla.org/en-US/docs/Web/Events/blur 39 | let useCapture = type === 'blur' || type === 'focus' 40 | 41 | window.$on(target, type, dispatchEvent, useCapture) 42 | } 43 | 44 | // Find the element's parent with the given tag name: 45 | // $parent(qs('a'), 'div'); 46 | window.$parent = function (element, tagName) { 47 | if (!element.parentNode) { 48 | return 49 | } 50 | 51 | if (element.parentNode.tagName.toLowerCase() === tagName.toLowerCase()) { 52 | return element.parentNode 53 | } 54 | 55 | return window.$parent(element.parentNode, tagName) 56 | } 57 | 58 | // Allow for looping on nodes by chaining: 59 | // qsa('.foo').forEach(function () {}) 60 | NodeList.prototype.forEach = Array.prototype.forEach 61 | })(window) 62 | -------------------------------------------------------------------------------- /app/assets/js/todo/model.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Forked from TodoMVC 3 | * https://todomvc.com 4 | * 5 | * MIT License © Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk 6 | */ 7 | 8 | (function (window) { 9 | 'use strict' 10 | 11 | /** 12 | * Creates a new Model instance and hooks up the storage. 13 | * 14 | * @constructor 15 | * @param {object} storage A reference to the client side storage class 16 | */ 17 | function Model (storage) { 18 | this.storage = storage 19 | } 20 | 21 | /** 22 | * Creates a new todo model 23 | * 24 | * @param {string} [title] The title of the task 25 | * @param {function} [callback] The callback to fire after the model is created 26 | */ 27 | Model.prototype.create = function (title, callback) { 28 | title = title || '' 29 | callback = callback || function () {} 30 | 31 | let newItem = { 32 | title: title.trim(), 33 | completed: false, 34 | } 35 | 36 | this.storage.save(newItem, callback) 37 | } 38 | 39 | /** 40 | * Finds and returns a model in storage. If no query is given it'll simply 41 | * return everything. If you pass in a string or number it'll look that up as 42 | * the ID of the model to find. Lastly, you can pass it an object to match 43 | * against. 44 | * 45 | * @param {string|number|object} [query] A query to match models against 46 | * @param {function} [callback] The callback to fire after the model is found 47 | * 48 | * @example 49 | * model.read(1, func); // Will find the model with an ID of 1 50 | * model.read('1'); // Same as above 51 | * //Below will find a model with foo equalling bar and hello equalling world. 52 | * model.read({ foo: 'bar', hello: 'world' }); 53 | */ 54 | Model.prototype.read = function (query, callback) { 55 | let queryType = typeof query 56 | 57 | callback = callback || function () {} 58 | 59 | if (queryType === 'function') { 60 | callback = query 61 | 62 | return this.storage.findAll(callback) 63 | } 64 | 65 | if (queryType === 'string' || queryType === 'number') { 66 | query = parseInt(query, 10) 67 | this.storage.find({ id: query }, callback) 68 | } else { 69 | this.storage.find(query, callback) 70 | } 71 | } 72 | 73 | /** 74 | * Updates a model by giving it an ID, data to update, and a callback to fire when 75 | * the update is complete. 76 | * 77 | * @param {number} id The id of the model to update 78 | * @param {object} data The properties to update and their new value 79 | * @param {function} callback The callback to fire when the update is complete. 80 | */ 81 | Model.prototype.update = function (id, data, callback) { 82 | this.storage.save(data, callback, id) 83 | } 84 | 85 | /** 86 | * Removes a model from storage 87 | * 88 | * @param {number} id The ID of the model to remove 89 | * @param {function} callback The callback to fire when the removal is complete. 90 | */ 91 | Model.prototype.remove = function (id, callback) { 92 | this.storage.remove(id, callback) 93 | } 94 | 95 | /** 96 | * WARNING: Will remove ALL data from storage. 97 | * 98 | * @param {function} callback The callback to fire when the storage is wiped. 99 | */ 100 | Model.prototype.removeAll = function (callback) { 101 | this.storage.drop(callback) 102 | } 103 | 104 | /** 105 | * Returns a count of all todos 106 | */ 107 | Model.prototype.getCount = function (callback) { 108 | let todos = { 109 | active: 0, 110 | completed: 0, 111 | total: 0, 112 | } 113 | 114 | this.storage.findAll(function (data) { 115 | data.forEach(function (todo) { 116 | if (todo.completed) { 117 | todos.completed++ 118 | } else { 119 | todos.active++ 120 | } 121 | 122 | todos.total++ 123 | }) 124 | 125 | callback(todos) 126 | }) 127 | } 128 | 129 | // Export to window 130 | window.app = window.app || {} 131 | window.app.Model = Model 132 | })(window) 133 | -------------------------------------------------------------------------------- /app/assets/js/todo/store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Forked from TodoMVC 3 | * https://todomvc.com 4 | * 5 | * MIT License © Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk 6 | */ 7 | 8 | /*jshint eqeqeq:false */ 9 | (function (window) { 10 | 'use strict' 11 | 12 | /** 13 | * Creates a new client side storage object and will create an empty 14 | * collection if no collection already exists. 15 | * 16 | * @param {string} name The name of our DB we want to use 17 | * @param {function} callback Our fake DB uses callbacks because in 18 | * real life you probably would be making AJAX calls 19 | */ 20 | function Store (name, callback) { 21 | callback = callback || function () {} 22 | 23 | this._dbName = name 24 | 25 | if (!localStorage.getItem(name)) { 26 | let todos = [] 27 | 28 | localStorage.setItem(name, JSON.stringify(todos)) 29 | } 30 | 31 | callback.call(this, JSON.parse(localStorage.getItem(name))) 32 | } 33 | 34 | /** 35 | * Finds items based on a query given as a JS object 36 | * 37 | * @param {object} query The query to match against (i.e. {foo: 'bar'}) 38 | * @param {function} callback The callback to fire when the query has 39 | * completed running 40 | * 41 | * @example 42 | * db.find({foo: 'bar', hello: 'world'}, function (data) { 43 | * // data will return any items that have foo: bar and 44 | * // hello: world in their properties 45 | * }); 46 | */ 47 | Store.prototype.find = function (query, callback) { 48 | if (!callback) { 49 | return 50 | } 51 | 52 | let todos = JSON.parse(localStorage.getItem(this._dbName)) 53 | 54 | callback.call(this, todos.filter(function (todo) { 55 | for (let q in query) { 56 | if (query[q] !== todo[q]) { 57 | return false 58 | } 59 | } 60 | 61 | return true 62 | })) 63 | } 64 | 65 | /** 66 | * Will retrieve all data from the collection 67 | * 68 | * @param {function} callback The callback to fire upon retrieving data 69 | */ 70 | Store.prototype.findAll = function (callback) { 71 | callback = callback || function () {} 72 | callback.call(this, JSON.parse(localStorage.getItem(this._dbName))) 73 | } 74 | 75 | /** 76 | * Will save the given data to the DB. If no item exists it will create a new 77 | * item, otherwise it'll simply update an existing item's properties 78 | * 79 | * @param {object} updateData The data to save back into the DB 80 | * @param {function} callback The callback to fire after saving 81 | * @param {number} id An optional param to enter an ID of an item to update 82 | */ 83 | Store.prototype.save = function (updateData, callback, id) { 84 | let todos = JSON.parse(localStorage.getItem(this._dbName)) 85 | 86 | callback = callback || function () {} 87 | 88 | // If an ID was actually given, find the item and update each property 89 | if (id) { 90 | for (let i = 0; i < todos.length; i++) { 91 | if (todos[i].id === id) { 92 | for (let key in updateData) { 93 | todos[i][key] = updateData[key] 94 | } 95 | break 96 | } 97 | } 98 | 99 | localStorage.setItem(this._dbName, JSON.stringify(todos)) 100 | callback.call(this, todos) 101 | } else { 102 | // Generate an ID 103 | updateData.id = new Date().getTime() 104 | 105 | todos.push(updateData) 106 | localStorage.setItem(this._dbName, JSON.stringify(todos)) 107 | callback.call(this, [updateData]) 108 | } 109 | } 110 | 111 | /** 112 | * Will remove an item from the Store based on its ID 113 | * 114 | * @param {number} id The ID of the item you want to remove 115 | * @param {function} callback The callback to fire after saving 116 | */ 117 | Store.prototype.remove = function (id, callback) { 118 | let todos = JSON.parse(localStorage.getItem(this._dbName)) 119 | 120 | for (let i = 0; i < todos.length; i++) { 121 | if (todos[i].id === id) { 122 | todos.splice(i, 1) 123 | break 124 | } 125 | } 126 | 127 | localStorage.setItem(this._dbName, JSON.stringify(todos)) 128 | callback.call(this, todos) 129 | } 130 | 131 | /** 132 | * Will drop all storage and start fresh 133 | * 134 | * @param {function} callback The callback to fire after dropping the data 135 | */ 136 | Store.prototype.drop = function (callback) { 137 | let todos = [] 138 | 139 | localStorage.setItem(this._dbName, JSON.stringify(todos)) 140 | callback.call(this, todos) 141 | } 142 | 143 | // Export to window 144 | window.app = window.app || {} 145 | window.app.Store = Store 146 | })(window) 147 | -------------------------------------------------------------------------------- /app/assets/js/todo/template.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Forked from TodoMVC 3 | * https://todomvc.com 4 | * 5 | * MIT License © Addy Osmani, Sindre Sorhus, Pascal Hartig, Stephen Sawchuk 6 | */ 7 | 8 | /*jshint laxbreak:true */ 9 | (function (window) { 10 | 'use strict' 11 | 12 | let htmlEscapes = { 13 | '&': '&', 14 | '<': '<', 15 | '>': '>', 16 | '"': '"', 17 | '\'': ''', 18 | '`': '`', 19 | } 20 | 21 | let escapeHtmlChar = function (chr) { 22 | return htmlEscapes[chr] 23 | } 24 | 25 | let reUnescapedHtml = /[&<>"'`]/g 26 | let reHasUnescapedHtml = new RegExp(reUnescapedHtml.source) 27 | 28 | let escape = function (string) { 29 | return (string && reHasUnescapedHtml.test(string)) 30 | ? string.replace(reUnescapedHtml, escapeHtmlChar) 31 | : string 32 | } 33 | 34 | /** 35 | * Sets up defaults for all the Template methods such as a default template 36 | * 37 | * @constructor 38 | */ 39 | function Template () { 40 | this.defaultTemplate 41 | = 'To alias a route or DOM element for use later, use the .as()
command.
78 | // The following DOM command chain is unwieldy.
79 | // To avoid repeating it, let's use `.as()`!
80 | cy.get('.as-table')
81 | .find('tbody>tr').first()
82 | .find('td').first()
83 | .find('button').as('firstBtn')
84 |
85 | // To reference the alias we created, we place an
86 | // @ in front of its name
87 | cy.get('@firstBtn').click()
88 |
89 | cy.get('@firstBtn')
90 | .should('have.class', 'btn-success')
91 | .and('contain', 'Changed')
92 |
93 | // Alias the route to wait for its response
94 |
95 | cy.intercept('GET', '**/comments/*').as('getComment')
96 |
97 | // we have code that gets a comment when
98 | // the button is clicked in scripts.js
99 | cy.get('.network-btn').click()
100 |
101 | // https://on.cypress.io/wait
102 | cy.wait('@getComment').its('response.statusCode').should('eq', 200)
103 |
104 | Column 1 | 113 |Column 2 | 114 |
---|---|
119 | Row 1: Cell 1 120 | | 121 |122 | Row 1: Cell 2 123 | | 124 |
127 | Row 2: Cell 1 128 | | 129 |130 | Row 2: Cell 2 131 | | 132 |
To set the viewport size and dimension, use the cy.viewport()
command.
cy.get('#navbar').should('be.visible')
78 | cy.viewport(320, 480)
79 |
80 | // the navbar should have collapse since our screen is smaller
81 | cy.get('#navbar').should('not.be.visible')
82 | cy.get('.navbar-toggle').should('be.visible').click()
83 | cy.get('.nav').find('a').should('be.visible')
84 |
85 | // lets see what our app looks like on a super large screen
86 | cy.viewport(2999, 2999)
87 |
88 | // cy.viewport() accepts a set of preset sizes
89 | // to easily set the screen to a device's width and height
90 |
91 | // We added a cy.wait() between each viewport change so you can see
92 | // the change otherwise it is a little too fast to see :)
93 |
94 | cy.viewport('macbook-15')
95 | cy.wait(200)
96 | cy.viewport('macbook-13')
97 | cy.wait(200)
98 | cy.viewport('macbook-11')
99 | cy.wait(200)
100 | cy.viewport('ipad-2')
101 | cy.wait(200)
102 | cy.viewport('ipad-mini')
103 | cy.wait(200)
104 | cy.viewport('iphone-6+')
105 | cy.wait(200)
106 | cy.viewport('iphone-6')
107 | cy.wait(200)
108 | cy.viewport('iphone-5')
109 | cy.wait(200)
110 | cy.viewport('iphone-4')
111 | cy.wait(200)
112 | cy.viewport('iphone-3')
113 | cy.wait(200)
114 |
115 | // cy.viewport() accepts an orientation for all presets
116 | // the default orientation is 'portrait'
117 | cy.viewport('ipad-2', 'portrait')
118 | cy.wait(200)
119 | cy.viewport('iphone-4', 'landscape')
120 | cy.wait(200)
121 |
122 | // The viewport will be reset back to the default dimensions
123 | // in between tests (the default can be set in cypress.config.js)
124 | To wait for a specific amount of time or resource to resolve, use the cy.wait()
command.
cy.get('.wait-input1').type('Wait 1000ms after typing')
77 | cy.wait(1000)
78 | cy.get('.wait-input2').type('Wait 1000ms after typing')
79 | cy.wait(1000)
80 | cy.get('.wait-input3').type('Wait 1000ms after typing')
81 | cy.wait(1000)
82 |
83 | // Listen to GET to comments/1
84 | cy.intercept('GET', '**/comments/*').as('getComment')
85 |
86 | // we have code that gets a comment when
87 | // the button is clicked in scripts.js
88 | cy.get('.network-btn').click()
89 |
90 | // wait for GET comments/1
91 | cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])
92 | To get the global window object, use the cy.window()
command.
cy.window().should('have.property', 'top')
78 | To get the document object, use the cy.document()
command.
cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
86 | To get the title, use the cy.title()
command.
cy.title().should('include', 'Kitchen Sink')
94 |