diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..f2adaaa --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "plugins": [ + "istanbul" + ] +} diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 1aabdfb..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: 2.1 - -default_test_steps: &default_test_steps - steps: - - checkout - - run: yarn - - run: yarn test - -jobs: - test: - docker: - - image: circleci/node:10-browsers - environment: - PERCY_ENABLE: 0 - <<: *default_test_steps - percy_test: - # We've opted this node version to be the one that runs and reports Percy's status - docker: - - image: circleci/node:12-browsers - <<: *default_test_steps - release: - docker: - - image: circleci/node:12 - steps: - - checkout - - run: yarn - - run: npx semantic-release - -workflows: - version: 2.1 - test_and_release: - jobs: - - test - - percy_test - - release: - requires: - - test - - percy_test diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..702dcb6 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,15 @@ +extends: standard +plugins: + - cypress +env: + cypress/globals: true +rules: + prefer-const: off + no-unused-expressions: off + promise/param-names: off + semi: [error, always] + space-before-function-paren: + - error + - anonymous: never + asyncArrow: always + named: never diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3dc6a0f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: +- package-ecosystem: npm + directory: / + schedule: + interval: weekly + commit-message: + prefix: ⬆️ diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..f98c9e9 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,31 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '💥 Breaking Changes' + labels: + - breaking + - title: '✨ Enhancements' + labels: + - feature + - enhancement + - title: '🐛 Bug Fixes' + labels: + - fix + - bugfix + - bug + - title: '🏗 Maintenance' + labels: + - chore + - dependencies +change-title-escapes: '\<*_&#@' +version-resolver: + major: + labels: + - breaking + minor: + labels: + - feature + - enhancement + default: patch +template: '$CHANGES' +prerelease: true diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000..ef80779 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,11 @@ +name: Changelog +on: + push: + branches: [master] +jobs: + update_draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..b32862d --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,21 @@ +name: Lint +on: push +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: v1/${{ runner.os }}/node-12/${{ hashFiles('**/yarn.lock') }} + restore-keys: v1/${{ runner.os }}/node-12/ + - run: yarn + - run: yarn lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..443af54 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Release +on: + release: + types: [published] +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + registry-url: 'https://registry.npmjs.org' + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: v1/${{ runner.os }}/node-12/${{ hashFiles('**/yarn.lock') }} + restore-keys: v1/${{ runner.os }}/node-12/ + - run: yarn + - run: yarn build + - run: npm publish --tag next + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b6d5ef6 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Test +on: push +jobs: + test: + name: Test + strategy: + matrix: + os: [ubuntu-latest] + node: [12] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: v1/${{ runner.os }}/node-${{ matrix.node }}/${{ hashFiles('**/yarn.lock') }} + restore-keys: v1/${{ runner.os }}/node-${{ matrix.node }}/ + - run: yarn + - run: yarn test:coverage diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 0000000..1648474 --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,21 @@ +name: Typecheck +on: push +jobs: + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: 12 + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + - uses: actions/cache@v2 + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: v1/${{ runner.os }}/node-12/${{ hashFiles('**/yarn.lock') }} + restore-keys: v1/${{ runner.os }}/node-12/ + - run: yarn + - run: yarn test:types diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml deleted file mode 100644 index 842e6e0..0000000 --- a/.github/workflows/windows.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Windows CI -on: [push, pull_request] -jobs: - build: - runs-on: windows-2019 - steps: - - name: Checkout - uses: actions/checkout@master - - name: Install - run: yarn - - name: Build - run: yarn build - - name: Percy Test - uses: percy/exec-action@master - with: - command: "node run-tests.js" - env: - PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} diff --git a/.gitignore b/.gitignore index 5b3ce88..f806226 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -node_modules/ -dist/ -cypress/screenshots +node_modules +.nyc_output +coverage +cypress/fixtures diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 81b854d..0000000 --- a/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -lib/ -cypress/ -cypress.json diff --git a/.nycrc b/.nycrc new file mode 100644 index 0000000..2f62cb3 --- /dev/null +++ b/.nycrc @@ -0,0 +1,6 @@ +{ + "branches": 100, + "lines": 100, + "functions": 100, + "statements": 100 +} diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 81f0d70..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,106 +0,0 @@ -## [2.3.4](https://github.com/percy/percy-cypress/compare/v2.3.3...v2.3.4) (2021-01-21) - - -### Bug Fixes - -* Update Cypress `peerDependency` for Cypress 6 ([#288](https://github.com/percy/percy-cypress/issues/288)) ([6991e44](https://github.com/percy/percy-cypress/commit/6991e44ae73ed756c12d730222f4e917ab07695c)) - -## [2.3.3](https://github.com/percy/percy-cypress/compare/v2.3.2...v2.3.3) (2020-12-01) - - -### Bug Fixes - -* Upgrade deps for security ([#277](https://github.com/percy/percy-cypress/issues/277)) ([98b06a4](https://github.com/percy/percy-cypress/commit/98b06a4)) - -## [2.3.2](https://github.com/percy/percy-cypress/compare/v2.3.1...v2.3.2) (2020-09-30) - - -### Bug Fixes - -* Add cypress 5 to peerDependencies ([#250](https://github.com/percy/percy-cypress/issues/250)) ([98d982d](https://github.com/percy/percy-cypress/commit/98d982d)) - -## [2.3.1](https://github.com/percy/percy-cypress/compare/v2.3.0...v2.3.1) (2020-03-05) - - -### Bug Fixes - -* Update peerDependencies for Cypress v4 ([#198](https://github.com/percy/percy-cypress/issues/198)) ([0502199](https://github.com/percy/percy-cypress/commit/0502199)), closes [#193](https://github.com/percy/percy-cypress/issues/193) - -# [2.3.0](https://github.com/percy/percy-cypress/compare/v2.2.0...v2.3.0) (2020-02-03) - - -### Features - -* Use node v10 ([#190](https://github.com/percy/percy-cypress/issues/190)) ([be177c7](https://github.com/percy/percy-cypress/commit/be177c7)) - -# [2.2.0](https://github.com/percy/percy-cypress/compare/v2.1.1...v2.2.0) (2019-10-08) - - -### Features - -* :sparkles: Add request headers option for asset-discovery ([#159](https://github.com/percy/percy-cypress/issues/159)) ([a595f48](https://github.com/percy/percy-cypress/commit/a595f48)) - -## [2.1.1](https://github.com/percy/percy-cypress/compare/v2.1.0...v2.1.1) (2019-09-20) - - -### Bug Fixes - -* add `percyCSS` option to types ([#156](https://github.com/percy/percy-cypress/issues/156)) ([7475783](https://github.com/percy/percy-cypress/commit/7475783)) - -# [2.1.0](https://github.com/percy/percy-cypress/compare/v2.0.1...v2.1.0) (2019-09-17) - - -### Features - -* Add the ability to pass per-snapshot CSS ([#154](https://github.com/percy/percy-cypress/issues/154)) ([02b976f](https://github.com/percy/percy-cypress/commit/02b976f)) - -## [2.0.1](https://github.com/percy/percy-cypress/compare/v2.0.0...v2.0.1) (2019-08-26) - - -### Bug Fixes - -* pass minHeight option along to agent ([#148](https://github.com/percy/percy-cypress/issues/148)) ([44c2152](https://github.com/percy/percy-cypress/commit/44c2152)) - -# [2.0.0](https://github.com/percy/percy-cypress/compare/v1.0.9...v2.0.0) (2019-08-02) - - -### Bug Fixes - -* Use `cy.task` for health check rather than `cy.exec` (BREAKING CHANGE) ([#140](https://github.com/percy/percy-cypress/issues/140)) ([40550f7](https://github.com/percy/percy-cypress/commit/40550f7)), closes [#104](https://github.com/percy/percy-cypress/issues/104) [#104](https://github.com/percy/percy-cypress/issues/104) [#138](https://github.com/percy/percy-cypress/issues/138) - - -### BREAKING CHANGES - -* ## The problem - -In all of the Percy SDKs we do a thing called a "heath check", which makes sure the Percy agent server is open and ready to accept the DOM we're going to `POST` to it. If the health check fails, we will disable Percy in the SDK so we're not failing your tests due to Percy not running. - -In the Cypress SDK, this was implemented in an interesting way due a limitation with `cy.request`. The TL:DR of that is we can't `.catch` a failed request, so we needed to use `cy.exec` to health check & gracefully fall out. You can read a little more about that limitation here: https://github.com/cypress-io/cypress/issues/3161 - -## [1.0.9](https://github.com/percy/percy-cypress/compare/v1.0.8...v1.0.9) (2019-05-08) - - -### Bug Fixes - -* Add TypeScript types. Closes [#89](https://github.com/percy/percy-cypress/issues/89) ([#96](https://github.com/percy/percy-cypress/issues/96)) ([6548bf7](https://github.com/percy/percy-cypress/commit/6548bf7)), closes [/github.com/percy/percy-cypress/pull/96#issuecomment-490487665](https://github.com//github.com/percy/percy-cypress/pull/96/issues/issuecomment-490487665) - -## [1.0.8](https://github.com/percy/percy-cypress/compare/v1.0.7...v1.0.8) (2019-05-02) - - -### Bug Fixes - -* Properly pass user agent ([#93](https://github.com/percy/percy-cypress/issues/93)) ([9b9f5e6](https://github.com/percy/percy-cypress/commit/9b9f5e6)) - -## [1.0.7](https://github.com/percy/percy-cypress/compare/v1.0.6...v1.0.7) (2019-04-30) - - -### Bug Fixes - -* Open `@percy/agent` version range (tilde over caret) ([#92](https://github.com/percy/percy-cypress/issues/92)) ([0e4b3f9](https://github.com/percy/percy-cypress/commit/0e4b3f9)), closes [/github.com/npm/node-semver#caret-ranges-123-025-004](https://github.com//github.com/npm/node-semver/issues/caret-ranges-123-025-004) [/github.com/npm/node-semver#tilde-ranges-123-12-1](https://github.com//github.com/npm/node-semver/issues/tilde-ranges-123-12-1) - -## [1.0.6](https://github.com/percy/percy-cypress/compare/v1.0.5...v1.0.6) (2019-04-09) - - -### Bug Fixes - -* Require @percy/agent 0.2.2. Resolves issue [#58](https://github.com/percy/percy-cypress/issues/58) ([#80](https://github.com/percy/percy-cypress/issues/80)) ([15c59be](https://github.com/percy/percy-cypress/commit/15c59be)) diff --git a/README.md b/README.md index 7ec8c9e..c762108 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,97 @@ # @percy/cypress +[![Version](https://img.shields.io/npm/v/@percy/cypress.svg)](https://npmjs.org/package/@percy/cypress) +![Test](https://github.com/percy/percy-cypress/workflows/Test/badge.svg) -[![Package Status](https://img.shields.io/npm/v/@percy/cypress.svg)](https://www.npmjs.com/package/@percy/cypress) [![This project is using Percy.io for visual regression testing.](https://percy.io/static/images/percy-badge.svg)](https://percy.io/percy/percy-cypress) [![CircleCI](https://circleci.com/gh/percy/percy-cypress.svg?style=svg)](https://circleci.com/gh/percy/percy-cypress) +[Percy](https://percy.io) visual testing for [Cypress](https://cypress.io). -[Percy](https://percy.io) visual testing for [Cypress](https://www.cypress.io). +## Installation -## Resources +```sh-session +$ npm install --save-dev @percy/cli @percy/cypress@next +``` -* [Setup and integration docs](https://docs.percy.io/docs/cypress) -* [Tutorial](https://docs.percy.io/docs/cypress-tutorial) -* [Example integration](https://github.com/percy/example-percy-cypress) -* [Example integration with TypeScript](https://github.com/percy/example-percy-cypress-typescript) +Then add to your `cypress/support/index.js` file + +```javascript +import '@percy/cypress' +``` + +## Usage + +This is an example using the `cy.percySnapshot` command. + +```javascript +describe('My app', () => { + it('should look good', () => { + cy.get('body').should('have.class', 'finished-loading'); + cy.percySnapshot(); + + cy.get('button').click(); + cy.percySnapshot('Clicked button'); + }); +}); +``` + +Running the test above will result in the following log: + +```sh-session +$ cypress run +... +[percy] Percy is not running, disabling snapshots +``` + +When running with [`percy +exec`](https://github.com/percy/cli/tree/master/packages/cli-exec#percy-exec), and your project's +`PERCY_TOKEN`, a new Percy build will be created and snapshots will be uploaded to your project. + +```sh-session +$ export PERCY_TOKEN=[your-project-token] +$ percy exec -- cypress run +[percy] Percy has started! +[percy] Created build #1: https://percy.io/[your-project] +[percy] Running "cypress run" +... +[percy] Snapshot taken "My app should look good" +[percy] Snapshot taken "Clicked button" +... +[percy] Stopping percy... +[percy] Finalized build #1: https://percy.io/[your-project] +[percy] Done! +``` + +## Configuration + +`cy.percySnapshot([name][, options])` + +- `name` - The snapshot name; must be unique to each snapshot; defaults to the full test title +- `options` - Additional snapshot options (overrides any project options) + - `options.widths` - An array of widths to take screenshots at + - `options.minHeight` - The minimum viewport height to take screenshots at + - `options.percyCSS` - Percy specific CSS only applied in Percy's rendering environment + - `options.requestHeaders` - Headers that should be used during asset discovery + - `options.enableJavaScript` - Enable JavaScript in Percy's rendering environment + +## Upgrading + +#### Installing `@percy/cli` + +If you're coming from a pre-3.0 version of this package, make sure to install `@percy/cli` after +upgrading to retain any existing scripts that reference the Percy CLI command. + +```sh-session +$ npm install --save-dev @percy/cli +``` + +#### Removing tasks + +If you're coming from 2.x the health check task, `@percy/cypress/task`, is no longer needed and no +longer exists. You should remove this task from your `cypress/plugins/index.js` file. + +#### Migrating Config + +If you have a previous Percy configuration file, migrate it to the newest version with the +[`config:migrate`](https://github.com/percy/cli/tree/master/packages/cli-config#percy-configmigrate-filepath-output) command: + +```sh-session +$ percy config:migrate +``` diff --git a/RELEASING.md b/RELEASING.md deleted file mode 100644 index 5d94c2d..0000000 --- a/RELEASING.md +++ /dev/null @@ -1,3 +0,0 @@ -# Releasing - -Releasing of the `@percy/cypress` package is handled automatically with the [semantic-release](https://github.com/semantic-release/semantic-release) package. diff --git a/cypress.json b/cypress.json index 60ed5aa..8513da0 100644 --- a/cypress.json +++ b/cypress.json @@ -1,3 +1,4 @@ { - "video": false + "video": false, + "screenshotOnRunFailure": false } diff --git a/cypress/integration/index.test.js b/cypress/integration/index.test.js new file mode 100644 index 0000000..352e1ae --- /dev/null +++ b/cypress/integration/index.test.js @@ -0,0 +1,68 @@ +import helpers from '@percy/sdk-utils/test/client'; + +const { match } = Cypress.sinon; + +describe('percySnapshot', () => { + beforeEach(() => { + cy.then(helpers.setup); + cy.visit('http://localhost:8000'); + cy.wrap(cy.spy(Cypress, 'log').log(false)).as('log'); + }); + + afterEach(() => { + cy.then(helpers.teardown); + }); + + it('disables snapshots when the healthcheck fails', () => { + cy.then(() => helpers.testFailure('/percy/healthcheck')); + + cy.percySnapshot(); + cy.percySnapshot('Snapshot 2'); + + cy.then(helpers.getRequests).should('deep.equal', [ + ['/percy/healthcheck'] + ]); + + cy.wrap(helpers.logger.stderr).should('deep.equal', []); + cy.wrap(helpers.logger.stdout).should('deep.equal', [ + '[percy] Percy is not running, disabling snapshots' + ]); + }); + + it('posts snapshots to the local percy server', () => { + cy.percySnapshot(); + cy.percySnapshot('Snapshot 2'); + + cy.then(helpers.getRequests).should(requests => { + // test stub so we can utilize sinon matchers + let test = cy.stub(); test(requests); + + expect(test).to.be.calledWith(match([ + match(['/percy/healthcheck']), + match(['/percy/dom.js']), + match(['/percy/snapshot', match({ + name: 'percySnapshot posts snapshots to the local percy server', + url: 'http://localhost:8000/', + domSnapshot: match(/(.*?)<\/head>Snapshot Me<\/body><\/html>/), + clientInfo: match(/@percy\/cypress\/.+/), + environmentInfo: match(/cypress\/.+/) + })]), + match(['/percy/snapshot', match({ + name: 'Snapshot 2' + })]) + ])); + }); + }); + + it('handles snapshot failures', () => { + cy.then(() => helpers.testFailure('/percy/snapshot')); + + cy.percySnapshot(); + + cy.wrap(helpers.logger.stdout).should('deep.equal', []); + cy.wrap(helpers.logger.stderr).should('deep.equal', [ + '[percy] Could not take DOM snapshot "percySnapshot handles snapshot failures"', + '[percy] Error: 500 Internal Server Error' + ]); + }); +}); diff --git a/cypress/integration/sdk_spec.js b/cypress/integration/sdk_spec.js deleted file mode 100644 index 6cc60d4..0000000 --- a/cypress/integration/sdk_spec.js +++ /dev/null @@ -1,77 +0,0 @@ -describe('@percy/cypress', function() { - - describe('with local app', function() { - - beforeEach(function() { - // Make sure we start each test with a clean slate. - cy.clearLocalStorage() - // Load the app and verify that it is in fact being loaded correctly. - cy.visit('http://localhost:8000') - cy.get('.todoapp').should('exist') - }) - - it('snapshots with autogenerated name', function() { - cy.percySnapshot() - }) - - it('snapshots with provided name', function() { - cy.percySnapshot('Explicitly given name') - }) - - it('snapshots with provided name and widths', function() { - cy.percySnapshot('Snapshot with widths', { widths: [768, 992, 1200] }) - }) - - it('snapshots with provided name and percyCSS', function() { - cy.percySnapshot('Snapshot with percyCSS', { - percyCSS: `body { background-color: purple; }` - }) - }) - - it('snapshots with provided name and minHeight', function() { - cy.get('.todoapp').should('exist') - cy.percySnapshot('Snapshot with minHeight', { minHeight: 2000 }) - }) - - it('correctly takes multiple snapshots in one test', function() { - // Enter a new todo. - cy.get('.new-todo').type('A thing to accomplish {enter}') - cy.get('.todo-count').should('contain', '1 item left') - cy.percySnapshot('Multiple snapshots #1 -- 1 todo left') - - // Click it off -- it should be marked as completed. - cy.get('.toggle').click() - cy.get('.todo-count').should('contain', '0 items left') - cy.percySnapshot('Multiple snapshots #2 -- 1 checked-off todo') - }) - - it('snapshots part of a page', function() { - cy.percySnapshot('With a passed domTransformation function', { - domTransformation: documentClone => scope(documentClone, 'footer.info') - }) - }) - }) - - describe('with live sites', function() { - // The tests in this block require setting '"chromeWebSecurity": false' in cypress.json. - // For details, see: https://docs.cypress.io/guides/guides/web-security.html#Disabling-Web-Security - - it('snapshots a website with HTTP', function() { - cy.visit('http://example.com/') - cy.percySnapshot('http://example.com/') - }) - - it('snapshots a website with HTTPS, strict CSP, CORS and HSTS setup', function() { - cy.visit('https://sdk-test.percy.dev') - cy.percySnapshot('https://sdk-test.percy.dev') - }) - }) -}) - - -function scope(documentClone, selector) { - let element = documentClone.querySelector(selector); - documentClone.querySelector('body').innerHTML = element.outerHTML; - - return documentClone; -} diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index 8f3dde3..29f8eee 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -1,28 +1,5 @@ -// *********************************************************** -// This example plugins/index.js can be used to load plugins -// -// You can change the location of this file or turn off loading -// the plugins file with the 'pluginsFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/plugins-guide -// *********************************************************** - -// This function is called when a project is opened or re-opened (e.g. due to -// the project's config changing) -const percyHealthCheck = require("../../task"); - module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config - - // Make it possible to log things to stdout by calling 'cy.task('log', 'some message to log'). - // Useful for development and debugging. - on("task", { - log(message) { - console.log(message); - return null; - } - }); - on("task", percyHealthCheck); + require('@cypress/code-coverage/task')(on, config); + on('file:preprocessor', require('@cypress/code-coverage/use-babelrc')); + return config; }; diff --git a/cypress/support/commands.js b/cypress/support/commands.js deleted file mode 100644 index 4d78771..0000000 --- a/cypress/support/commands.js +++ /dev/null @@ -1,27 +0,0 @@ -// *********************************************** -// This example commands.js shows you how to -// create various custom commands and overwrite -// existing commands. -// -// For more comprehensive examples of custom -// commands please read more here: -// https://on.cypress.io/custom-commands -// *********************************************** -// -// -// -- This is a parent command -- -// Cypress.Commands.add("login", (email, password) => { ... }) -// -// -// -- This is a child command -- -// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) - -import '../../dist/index' diff --git a/cypress/support/index.js b/cypress/support/index.js index d68db96..6d36470 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -1,20 +1,2 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' - -// Alternatively you can use CommonJS syntax: -// require('./commands') +import '@cypress/code-coverage/support'; +import '../..'; diff --git a/cypress/testapp/README.md b/cypress/testapp/README.md deleted file mode 100644 index 4e4badd..0000000 --- a/cypress/testapp/README.md +++ /dev/null @@ -1,11 +0,0 @@ -App for testing the SDK. Based on the -[TodoMVC](https://github.com/tastejs/todomvc) -[Vanilla-ES6](https://github.com/tastejs/todomvc/tree/master/examples/vanilla-es6) -app, forked at commit -[c78ae12a1834a11da6236c64a0c0fb06b20b7c51](https://github.com/tastejs/todomvc/tree/c78ae12a1834a11da6236c64a0c0fb06b20b7c51). - -To see the app in action: - -```bash -$ open index.html -``` diff --git a/cypress/testapp/base.css b/cypress/testapp/base.css deleted file mode 100644 index da65968..0000000 --- a/cypress/testapp/base.css +++ /dev/null @@ -1,141 +0,0 @@ -hr { - margin: 20px 0; - border: 0; - border-top: 1px dashed #c5c5c5; - border-bottom: 1px dashed #f7f7f7; -} - -.learn a { - font-weight: normal; - text-decoration: none; - color: #b83f45; -} - -.learn a:hover { - text-decoration: underline; - color: #787e7e; -} - -.learn h3, -.learn h4, -.learn h5 { - margin: 10px 0; - font-weight: 500; - line-height: 1.2; - color: #000; -} - -.learn h3 { - font-size: 24px; -} - -.learn h4 { - font-size: 18px; -} - -.learn h5 { - margin-bottom: 0; - font-size: 14px; -} - -.learn ul { - padding: 0; - margin: 0 0 30px 25px; -} - -.learn li { - line-height: 20px; -} - -.learn p { - font-size: 15px; - font-weight: 300; - line-height: 1.3; - margin-top: 0; - margin-bottom: 0; -} - -#issue-count { - display: none; -} - -.quote { - border: none; - margin: 20px 0 60px 0; -} - -.quote p { - font-style: italic; -} - -.quote p:before { - content: '“'; - font-size: 50px; - opacity: .15; - position: absolute; - top: -20px; - left: 3px; -} - -.quote p:after { - content: '”'; - font-size: 50px; - opacity: .15; - position: absolute; - bottom: -42px; - right: 3px; -} - -.quote footer { - position: absolute; - bottom: -40px; - right: 0; -} - -.quote footer img { - border-radius: 3px; -} - -.quote footer a { - margin-left: 5px; - vertical-align: middle; -} - -.speech-bubble { - position: relative; - padding: 10px; - background: rgba(0, 0, 0, .04); - border-radius: 5px; -} - -.speech-bubble:after { - content: ''; - position: absolute; - top: 100%; - right: 30px; - border: 13px solid transparent; - border-top-color: rgba(0, 0, 0, .04); -} - -.learn-bar > .learn { - position: absolute; - width: 272px; - top: 8px; - left: -300px; - padding: 10px; - border-radius: 5px; - background-color: rgba(255, 255, 255, .6); - transition-property: left; - transition-duration: 500ms; -} - -@media (min-width: 899px) { - .learn-bar { - width: auto; - padding-left: 300px; - } - - .learn-bar > .learn { - left: 8px; - } -} diff --git a/cypress/testapp/base.js b/cypress/testapp/base.js deleted file mode 100644 index a56b5aa..0000000 --- a/cypress/testapp/base.js +++ /dev/null @@ -1,249 +0,0 @@ -/* global _ */ -(function () { - 'use strict'; - - /* jshint ignore:start */ - // Underscore's Template Module - // Courtesy of underscorejs.org - var _ = (function (_) { - _.defaults = function (object) { - if (!object) { - return object; - } - for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) { - var iterable = arguments[argsIndex]; - if (iterable) { - for (var key in iterable) { - if (object[key] == null) { - object[key] = iterable[key]; - } - } - } - } - return object; - }; - - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g - }; - - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; - - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; - - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; - - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); - - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); - - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); - - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; - - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; - - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } - - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; - - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; - - return template; - }; - - return _; - })({}); - - if (location.hostname === 'todomvc.com') { - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-31081062-1', 'auto'); - ga('send', 'pageview'); - } - /* jshint ignore:end */ - - function redirect() { - if (location.hostname === 'tastejs.github.io') { - location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com'); - } - } - - function findRoot() { - var base = location.href.indexOf('examples/'); - return location.href.substr(0, base); - } - - function getFile(file, callback) { - if (!location.host) { - return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.'); - } - - var xhr = new XMLHttpRequest(); - - xhr.open('GET', findRoot() + file, true); - xhr.send(); - - xhr.onload = function () { - if (xhr.status === 200 && callback) { - callback(xhr.responseText); - } - }; - } - - function Learn(learnJSON, config) { - if (!(this instanceof Learn)) { - return new Learn(learnJSON, config); - } - - var template, framework; - - if (typeof learnJSON !== 'object') { - try { - learnJSON = JSON.parse(learnJSON); - } catch (e) { - return; - } - } - - if (config) { - template = config.template; - framework = config.framework; - } - - if (!template && learnJSON.templates) { - template = learnJSON.templates.todomvc; - } - - if (!framework && document.querySelector('[data-framework]')) { - framework = document.querySelector('[data-framework]').dataset.framework; - } - - this.template = template; - - if (learnJSON.backend) { - this.frameworkJSON = learnJSON.backend; - this.frameworkJSON.issueLabel = framework; - this.append({ - backend: true - }); - } else if (learnJSON[framework]) { - this.frameworkJSON = learnJSON[framework]; - this.frameworkJSON.issueLabel = framework; - this.append(); - } - - this.fetchIssueCount(); - } - - Learn.prototype.append = function (opts) { - var aside = document.createElement('aside'); - aside.innerHTML = _.template(this.template, this.frameworkJSON); - aside.className = 'learn'; - - if (opts && opts.backend) { - // Remove demo link - var sourceLinks = aside.querySelector('.source-links'); - var heading = sourceLinks.firstElementChild; - var sourceLink = sourceLinks.lastElementChild; - // Correct link path - var href = sourceLink.getAttribute('href'); - sourceLink.setAttribute('href', href.substr(href.lastIndexOf('http'))); - sourceLinks.innerHTML = heading.outerHTML + sourceLink.outerHTML; - } else { - // Localize demo links - var demoLinks = aside.querySelectorAll('.demo-link'); - Array.prototype.forEach.call(demoLinks, function (demoLink) { - if (demoLink.getAttribute('href').substr(0, 4) !== 'http') { - demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href')); - } - }); - } - - document.body.className = (document.body.className + ' learn-bar').trim(); - document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); - }; - - Learn.prototype.fetchIssueCount = function () { - var issueLink = document.getElementById('issue-count-link'); - if (issueLink) { - var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos'); - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, true); - xhr.onload = function (e) { - var parsedResponse = JSON.parse(e.target.responseText); - if (parsedResponse instanceof Array) { - var count = parsedResponse.length; - if (count !== 0) { - issueLink.innerHTML = 'This app has ' + count + ' open issues'; - document.getElementById('issue-count').style.display = 'inline'; - } - } - }; - xhr.send(); - } - }; - - redirect(); - getFile('learn.json', Learn); -})(); diff --git a/cypress/testapp/bundle.js b/cypress/testapp/bundle.js deleted file mode 100644 index 37a8ebb..0000000 --- a/cypress/testapp/bundle.js +++ /dev/null @@ -1,57 +0,0 @@ -var $jscomp={scope:{},getGlobal:function(a){return"undefined"!=typeof window&&window===a?a:"undefined"!=typeof global?global:a}};$jscomp.global=$jscomp.getGlobal(this);$jscomp.initSymbol=function(){$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol);$jscomp.initSymbol=function(){}};$jscomp.symbolCounter_=0;$jscomp.Symbol=function(a){return"jscomp_symbol_"+a+$jscomp.symbolCounter_++}; -$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();$jscomp.global.Symbol.iterator||($jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));$jscomp.initSymbolIterator=function(){}};$jscomp.makeIterator=function(a){$jscomp.initSymbolIterator();if(a[$jscomp.global.Symbol.iterator])return a[$jscomp.global.Symbol.iterator]();var b=0;return{next:function(){return b==a.length?{done:!0}:{done:!1,value:a[b++]}}}}; -$jscomp.arrayFromIterator=function(a){for(var b,c=[];!(b=a.next()).done;)c.push(b.value);return c};$jscomp.arrayFromIterable=function(a){return a instanceof Array?a:$jscomp.arrayFromIterator($jscomp.makeIterator(a))}; -$jscomp.inherits=function(a,b){function c(){}c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a;for(var d in b)if($jscomp.global.Object.defineProperties){var e=$jscomp.global.Object.getOwnPropertyDescriptor(b,d);e&&$jscomp.global.Object.defineProperty(a,d,e)}else a[d]=b[d]};$jscomp.array=$jscomp.array||{};$jscomp.array.done_=function(){return{done:!0,value:void 0}}; -$jscomp.array.arrayIterator_=function(a,b){a instanceof String&&(a=String(a));var c=0;$jscomp.initSymbol();$jscomp.initSymbolIterator();var d={},e=(d.next=function(){if(cb;)--c in this?this[--a]=this[c]:delete this[a];return this};$jscomp.array.copyWithin$install=function(){Array.prototype.copyWithin||(Array.prototype.copyWithin=$jscomp.array.copyWithin)}; -$jscomp.array.fill=function(a,b,c){null!=c&&a.length||(c=this.length||0);c=Number(c);for(b=Number((void 0===b?0:b)||0);b>>0;if(0===a)return 32;var b=0;0===(a&4294901760)&&(a<<=16,b+=16);0===(a&4278190080)&&(a<<=8,b+=8);0===(a&4026531840)&&(a<<=4,b+=4);0===(a&3221225472)&&(a<<=2,b+=2);0===(a&2147483648)&&b++;return b};$jscomp.math.imul=function(a,b){a=Number(a);b=Number(b);var c=a&65535,d=b&65535;return c*d+((a>>>16&65535)*d+c*(b>>>16&65535)<<16>>>0)|0};$jscomp.math.sign=function(a){a=Number(a);return 0===a||isNaN(a)?a:0a&&-.25a&&-.25a?-b:b};$jscomp.math.acosh=function(a){a=Number(a);return Math.log(a+Math.sqrt(a*a-1))};$jscomp.math.asinh=function(a){a=Number(a);if(0===a)return a;var b=Math.log(Math.abs(a)+Math.sqrt(a*a+1));return 0>a?-b:b}; -$jscomp.math.atanh=function(a){a=Number(a);return($jscomp.math.log1p(a)-$jscomp.math.log1p(-a))/2}; -$jscomp.math.hypot=function(a,b,c){for(var d=[],e=2;ef){a/=f;b/=f;g=a*a+b*b;d=$jscomp.makeIterator(d);for(e=d.next();!e.done;e=d.next())e=e.value,e=Number(e)/f,g+=e*e;return Math.sqrt(g)*f}f=a*a+b*b;d=$jscomp.makeIterator(d);for(e=d.next();!e.done;e=d.next())e=e.value,e=Number(e),f+= -e*e;return Math.sqrt(f)};$jscomp.math.trunc=function(a){a=Number(a);if(isNaN(a)||Infinity===a||-Infinity===a||0===a)return a;var b=Math.floor(Math.abs(a));return 0>a?-b:b};$jscomp.math.cbrt=function(a){if(0===a)return a;a=Number(a);var b=Math.pow(Math.abs(a),1/3);return 0>a?-b:b};$jscomp.number=$jscomp.number||{};$jscomp.number.isFinite=function(a){return"number"!==typeof a?!1:!isNaN(a)&&Infinity!==a&&-Infinity!==a}; -$jscomp.number.isInteger=function(a){return $jscomp.number.isFinite(a)?a===Math.floor(a):!1};$jscomp.number.isNaN=function(a){return"number"===typeof a&&isNaN(a)};$jscomp.number.isSafeInteger=function(a){return $jscomp.number.isInteger(a)&&Math.abs(a)<=$jscomp.number.MAX_SAFE_INTEGER};$jscomp.number.EPSILON=Math.pow(2,-52);$jscomp.number.MAX_SAFE_INTEGER=9007199254740991;$jscomp.number.MIN_SAFE_INTEGER=-9007199254740991;$jscomp.object=$jscomp.object||{}; -$jscomp.object.assign=function(a,b){for(var c=[],d=1;dd||1114111=d?c+=String.fromCharCode(d):(d-=65536,c+=String.fromCharCode(d>>>10&1023|55296),c+=String.fromCharCode(d&1023|56320))}return c}; -$jscomp.string.repeat=function(a){var b=this.toString();if(0>a||1342177279>>=1)b+=b;return c};$jscomp.string.repeat$install=function(){String.prototype.repeat||(String.prototype.repeat=$jscomp.string.repeat)}; -$jscomp.string.codePointAt=function(a){var b=this.toString(),c=b.length;a=Number(a)||0;if(0<=a&&ad||56319a||57343=e};$jscomp.string.startsWith$install=function(){String.prototype.startsWith||(String.prototype.startsWith=$jscomp.string.startsWith)}; -$jscomp.string.endsWith=function(a,b){$jscomp.string.noRegExp_(a,"endsWith");var c=this.toString();a+="";void 0===b&&(b=c.length);for(var d=Math.max(0,Math.min(b|0,c.length)),e=a.length;0=e};$jscomp.string.endsWith$install=function(){String.prototype.endsWith||(String.prototype.endsWith=$jscomp.string.endsWith)}; -var module$src$item={},Item$$module$src$item,ItemList$$module$src$item,Empty$$module$src$item={Record:{}},EmptyItemQuery$$module$src$item,emptyItemQuery$$module$src$item=Empty$$module$src$item.Record,ItemQuery$$module$src$item,ItemUpdate$$module$src$item;module$src$item.emptyItemQuery=emptyItemQuery$$module$src$item;var module$src$store={},Store$$module$src$store=function(a,b){var c=window.localStorage,d;this.getLocalStorage=function(){return d||JSON.parse(c.getItem(a)||"[]")};this.setLocalStorage=function(b){c.setItem(a,JSON.stringify(d=b))};b&&b()};Store$$module$src$store.prototype.find=function(a,b){var c=this.getLocalStorage(),d;b(c.filter(function(b){for(d in a)if(a[d]!==b[d])return!1;return!0}))}; -Store$$module$src$store.prototype.update=function(a,b){for(var c=a.id,d=this.getLocalStorage(),e=d.length,f;e--;)if(d[e].id===c){for(f in a)d[e][f]=a[f];break}this.setLocalStorage(d);b&&b()};Store$$module$src$store.prototype.insert=function(a,b){var c=this.getLocalStorage();c.push(a);this.setLocalStorage(c);b&&b()};Store$$module$src$store.prototype.remove=function(a,b){var c,d=this.getLocalStorage().filter(function(b){for(c in a)if(a[c]!==b[c])return!0;return!1});this.setLocalStorage(d);b&&b(d)}; -Store$$module$src$store.prototype.count=function(a){this.find(module$src$item.emptyItemQuery,function(b){for(var c=b.length,d=c,e=0;d--;)e+=b[d].completed;a(c,c-e,e)})};module$src$store["default"]=Store$$module$src$store;var module$src$helpers={};function qs$$module$src$helpers(a,b){return(b||document).querySelector(a)}function $on$$module$src$helpers(a,b,c,d){a.addEventListener(b,c,!!d)}function $delegate$$module$src$helpers(a,b,c,d,e){$on$$module$src$helpers(a,c,function(c){for(var e=c.target,h=a.querySelectorAll(b),k=h.length;k--;)if(h[k]===e){d.call(e,c);break}},!!e)}var escapeForHTML$$module$src$helpers=function(a){return a.replace(/[&<]/g,function(a){return"&"===a?"&":"<"})};module$src$helpers.qs=qs$$module$src$helpers; -module$src$helpers.$on=$on$$module$src$helpers;module$src$helpers.$delegate=$delegate$$module$src$helpers;module$src$helpers.escapeForHTML=escapeForHTML$$module$src$helpers;var module$src$template={},Template$$module$src$template=function(){};Template$$module$src$template.prototype.itemList=function(a){return a.reduce(function(a,c){return a+('\n
  • \n\t
    \n\t\t\n\t\t\n\t\t\n\t
    \n
  • ')},"")}; -Template$$module$src$template.prototype.itemCounter=function(a){return a+" item"+(1!==a?"s":"")+" left"};module$src$template["default"]=Template$$module$src$template;var module$src$view={},_itemId$$module$src$view=function(a){return parseInt(a.parentNode.dataset.id||a.parentNode.parentNode.dataset.id,10)},ENTER_KEY$$module$src$view=13,ESCAPE_KEY$$module$src$view=27,View$$module$src$view=function(a){var b=this;this.template=a;this.$todoList=module$src$helpers.qs(".todo-list");this.$todoItemCounter=module$src$helpers.qs(".todo-count");this.$clearCompleted=module$src$helpers.qs(".clear-completed");this.$main=module$src$helpers.qs(".main");this.$toggleAll=module$src$helpers.qs(".toggle-all"); -this.$newTodo=module$src$helpers.qs(".new-todo");module$src$helpers.$delegate(this.$todoList,"li label","dblclick",function(a){b.editItem(a.target)})};View$$module$src$view.prototype.editItem=function(a){var b=a.parentElement.parentElement;b.classList.add("editing");var c=document.createElement("input");c.className="edit";c.value=a.innerText;b.appendChild(c);c.focus()};View$$module$src$view.prototype.showItems=function(a){this.$todoList.innerHTML=this.template.itemList(a)}; -View$$module$src$view.prototype.removeItem=function(a){(a=module$src$helpers.qs('[data-id="'+a+'"]'))&&this.$todoList.removeChild(a)};View$$module$src$view.prototype.setItemsLeft=function(a){this.$todoItemCounter.innerHTML=this.template.itemCounter(a)};View$$module$src$view.prototype.setClearCompletedButtonVisibility=function(a){this.$clearCompleted.style.display=a?"block":"none"};View$$module$src$view.prototype.setMainVisibility=function(a){this.$main.style.display=a?"block":"none"}; -View$$module$src$view.prototype.setCompleteAllCheckbox=function(a){this.$toggleAll.checked=!!a};View$$module$src$view.prototype.updateFilterButtons=function(a){module$src$helpers.qs(".filters .selected").className="";module$src$helpers.qs('.filters [href="#/'+a+'"]').className="selected"};View$$module$src$view.prototype.clearNewTodo=function(){this.$newTodo.value=""}; -View$$module$src$view.prototype.setItemComplete=function(a,b){var c=module$src$helpers.qs('[data-id="'+a+'"]');c&&(c.className=b?"completed":"",module$src$helpers.qs("input",c).checked=b)};View$$module$src$view.prototype.editItemDone=function(a,b){var c=module$src$helpers.qs('[data-id="'+a+'"]'),d=module$src$helpers.qs("input.edit",c);c.removeChild(d);c.classList.remove("editing");module$src$helpers.qs("label",c).textContent=b}; -View$$module$src$view.prototype.bindAddItem=function(a){module$src$helpers.$on(this.$newTodo,"change",function(b){(b=b.target.value.trim())&&a(b)})};View$$module$src$view.prototype.bindRemoveCompleted=function(a){module$src$helpers.$on(this.$clearCompleted,"click",a)};View$$module$src$view.prototype.bindToggleAll=function(a){module$src$helpers.$on(this.$toggleAll,"click",function(b){a(b.target.checked)})}; -View$$module$src$view.prototype.bindRemoveItem=function(a){module$src$helpers.$delegate(this.$todoList,".destroy","click",function(b){a(_itemId$$module$src$view(b.target))})};View$$module$src$view.prototype.bindToggleItem=function(a){module$src$helpers.$delegate(this.$todoList,".toggle","click",function(b){b=b.target;a(_itemId$$module$src$view(b),b.checked)})}; -View$$module$src$view.prototype.bindEditItemSave=function(a){module$src$helpers.$delegate(this.$todoList,"li .edit","blur",function(b){b=b.target;b.dataset.iscanceled||a(_itemId$$module$src$view(b),b.value.trim())},!0);module$src$helpers.$delegate(this.$todoList,"li .edit","keypress",function(a){var c=a.target;a.keyCode===ENTER_KEY$$module$src$view&&c.blur()})}; -View$$module$src$view.prototype.bindEditItemCancel=function(a){module$src$helpers.$delegate(this.$todoList,"li .edit","keyup",function(b){var c=b.target;b.keyCode===ESCAPE_KEY$$module$src$view&&(c.dataset.iscanceled=!0,c.blur(),a(_itemId$$module$src$view(c)))})};module$src$view["default"]=View$$module$src$view;var module$src$controller={},Controller$$module$src$controller=function(a,b){var c=this;this.store=a;this.view=b;b.bindAddItem(this.addItem.bind(this));b.bindEditItemSave(this.editItemSave.bind(this));b.bindEditItemCancel(this.editItemCancel.bind(this));b.bindRemoveItem(this.removeItem.bind(this));b.bindToggleItem(function(a,b){c.toggleCompleted(a,b);c._filter()});b.bindRemoveCompleted(this.removeCompletedItems.bind(this));b.bindToggleAll(this.toggleAll.bind(this));this._activeRoute="";this._lastActiveRoute= -null};Controller$$module$src$controller.prototype.setView=function(a){this._activeRoute=a=a.replace(/^#\//,"");this._filter();this.view.updateFilterButtons(a)};Controller$$module$src$controller.prototype.addItem=function(a){var b=this;this.store.insert({id:Date.now(),title:a,completed:!1},function(){b.view.clearNewTodo();b._filter(!0)})}; -Controller$$module$src$controller.prototype.editItemSave=function(a,b){var c=this;b.length?this.store.update({id:a,title:b},function(){c.view.editItemDone(a,b)}):this.removeItem(a)};Controller$$module$src$controller.prototype.editItemCancel=function(a){var b=this;this.store.find({id:a},function(c){b.view.editItemDone(a,c[0].title)})};Controller$$module$src$controller.prototype.removeItem=function(a){var b=this;this.store.remove({id:a},function(){b._filter();b.view.removeItem(a)})}; -Controller$$module$src$controller.prototype.removeCompletedItems=function(){this.store.remove({completed:!0},this._filter.bind(this))};Controller$$module$src$controller.prototype.toggleCompleted=function(a,b){var c=this;this.store.update({id:a,completed:b},function(){c.view.setItemComplete(a,b)})}; -Controller$$module$src$controller.prototype.toggleAll=function(a){var b=this;this.store.find({completed:!a},function(c){c=$jscomp.makeIterator(c);for(var d=c.next();!d.done;d=c.next())b.toggleCompleted(d.value.id,a)});this._filter()}; -Controller$$module$src$controller.prototype._filter=function(a){var b=this,c=this._activeRoute;(a||""!==this._lastActiveRoute||this._lastActiveRoute!==c)&&this.store.find({"":module$src$item.emptyItemQuery,active:{completed:!1},completed:{completed:!0}}[c],this.view.showItems.bind(this.view));this.store.count(function(a,c,f){b.view.setItemsLeft(c);b.view.setClearCompletedButtonVisibility(f);b.view.setCompleteAllCheckbox(f===a);b.view.setMainVisibility(a)});this._lastActiveRoute=c}; -module$src$controller["default"]=Controller$$module$src$controller;var store$$module$src$app=new module$src$store["default"]("todos-vanilla-es6"),template$$module$src$app=new module$src$template["default"],view$$module$src$app=new module$src$view["default"](template$$module$src$app),controller$$module$src$app=new module$src$controller["default"](store$$module$src$app,view$$module$src$app),setView$$module$src$app=function(){return controller$$module$src$app.setView(document.location.hash)};module$src$helpers.$on(window,"load",setView$$module$src$app); -module$src$helpers.$on(window,"hashchange",setView$$module$src$app); diff --git a/cypress/testapp/index.css b/cypress/testapp/index.css deleted file mode 100644 index 3ac79f0..0000000 --- a/cypress/testapp/index.css +++ /dev/null @@ -1,379 +0,0 @@ -html, -body { - margin: 0; - padding: 0; -} - -button { - margin: 0; - padding: 0; - border: 0; - background: none; - font-size: 100%; - vertical-align: baseline; - font-family: inherit; - font-weight: inherit; - color: inherit; - -webkit-appearance: none; - appearance: none; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -body { - font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; - line-height: 1.4em; - background: #f5f5f5; - color: #4d4d4d; - min-width: 230px; - max-width: 550px; - margin: 0 auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-weight: 300; -} - -:focus { - outline: 0; -} - -.hidden { - display: none; -} - -.todoapp { - background: #fff; - margin: 130px 0 40px 0; - position: relative; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 25px 50px 0 rgba(0, 0, 0, 0.1); -} - -.todoapp input::-webkit-input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -.todoapp input::-moz-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -.todoapp input::input-placeholder { - font-style: italic; - font-weight: 300; - color: #e6e6e6; -} - -.todoapp h1 { - position: absolute; - top: -155px; - width: 100%; - font-size: 100px; - font-weight: 100; - text-align: center; - color: rgba(175, 47, 47, 0.15); - -webkit-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - text-rendering: optimizeLegibility; -} - -.new-todo, -.edit { - position: relative; - margin: 0; - width: 100%; - font-size: 24px; - font-family: inherit; - font-weight: inherit; - line-height: 1.4em; - border: 0; - color: inherit; - padding: 6px; - border: 1px solid #999; - box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); - box-sizing: border-box; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.new-todo { - padding: 16px 16px 16px 60px; - border: none; - background: rgba(0, 0, 0, 0.003); - box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); -} - -.main { - position: relative; - z-index: 2; - border-top: 1px solid #e6e6e6; -} - -.toggle-all { - width: 1px; - height: 1px; - border: none; /* Mobile Safari */ - opacity: 0; - position: absolute; - right: 100%; - bottom: 100%; -} - -.toggle-all + label { - width: 60px; - height: 34px; - font-size: 0; - position: absolute; - top: -52px; - left: -13px; - -webkit-transform: rotate(90deg); - transform: rotate(90deg); -} - -.toggle-all + label:before { - content: '❯'; - font-size: 22px; - color: #e6e6e6; - padding: 10px 27px 10px 27px; -} - -.toggle-all:checked + label:before { - color: #737373; -} - -.todo-list { - margin: 0; - padding: 0; - list-style: none; -} - -.todo-list li { - position: relative; - font-size: 24px; - border-bottom: 1px solid #ededed; -} - -.todo-list li:last-child { - border-bottom: none; -} - -.todo-list li.editing { - border-bottom: none; - padding: 0; -} - -.todo-list li.editing .edit { - display: block; - width: 506px; - padding: 12px 16px; - margin: 0 0 0 43px; -} - -.todo-list li.editing .view { - display: none; -} - -.todo-list li .toggle { - text-align: center; - width: 40px; - /* auto, since non-WebKit browsers doesn't support input styling */ - height: auto; - position: absolute; - top: 0; - bottom: 0; - margin: auto 0; - border: none; /* Mobile Safari */ - -webkit-appearance: none; - appearance: none; -} - -.todo-list li .toggle { - opacity: 0; -} - -.todo-list li .toggle + label { - /* - Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433 - IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/ - */ - background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E'); - background-repeat: no-repeat; - background-position: center left; -} - -.todo-list li .toggle:checked + label { - background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E'); -} - -.todo-list li label { - word-break: break-all; - padding: 15px 15px 15px 60px; - display: block; - line-height: 1.2; - transition: color 0.4s; -} - -.todo-list li.completed label { - color: #d9d9d9; - text-decoration: line-through; -} - -.todo-list li .destroy { - display: none; - position: absolute; - top: 0; - right: 10px; - bottom: 0; - width: 40px; - height: 40px; - margin: auto 0; - font-size: 30px; - color: #cc9a9a; - margin-bottom: 11px; - transition: color 0.2s ease-out; -} - -.todo-list li .destroy:hover { - color: #af5b5e; -} - -.todo-list li .destroy:after { - content: '×'; -} - -.todo-list li:hover .destroy { - display: block; -} - -.todo-list li .edit { - display: none; -} - -.todo-list li.editing:last-child { - margin-bottom: -1px; -} - -.footer { - color: #777; - padding: 10px 15px; - height: 20px; - text-align: center; - border-top: 1px solid #e6e6e6; -} - -.footer:before { - content: ''; - position: absolute; - right: 0; - bottom: 0; - left: 0; - height: 50px; - overflow: hidden; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), - 0 8px 0 -3px #f6f6f6, - 0 9px 1px -3px rgba(0, 0, 0, 0.2), - 0 16px 0 -6px #f6f6f6, - 0 17px 2px -6px rgba(0, 0, 0, 0.2); -} - -.todo-count { - float: left; - text-align: left; -} - -.todo-count strong { - font-weight: 300; -} - -.filters { - margin: 0; - padding: 0; - list-style: none; - position: absolute; - right: 0; - left: 0; -} - -.filters li { - display: inline; -} - -.filters li a { - color: inherit; - margin: 3px; - padding: 3px 7px; - text-decoration: none; - border: 1px solid transparent; - border-radius: 3px; -} - -.filters li a:hover { - border-color: rgba(175, 47, 47, 0.1); -} - -.filters li a.selected { - border-color: rgba(175, 47, 47, 0.2); -} - -.clear-completed, -html .clear-completed:active { - float: right; - position: relative; - line-height: 20px; - text-decoration: none; - cursor: pointer; -} - -.clear-completed:hover { - text-decoration: underline; -} - -.info { - margin: 65px auto 0; - color: #bfbfbf; - font-size: 10px; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); - text-align: center; -} - -.info p { - line-height: 1; -} - -.info a { - color: inherit; - text-decoration: none; - font-weight: 400; -} - -.info a:hover { - text-decoration: underline; -} - -/* - Hack to remove background from Mobile Safari. - Can't use it globally since it destroys checkboxes in Firefox -*/ -@media screen and (-webkit-min-device-pixel-ratio:0) { - .toggle-all, - .todo-list li .toggle { - background: none; - } - - .todo-list li .toggle { - height: 40px; - } -} - -@media (max-width: 430px) { - .footer { - height: 50px; - } - - .filters { - bottom: 10px; - } -} diff --git a/cypress/testapp/index.html b/cypress/testapp/index.html deleted file mode 100644 index 5becede..0000000 --- a/cypress/testapp/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - Vanilla ES6 • TodoMVC - - - -
    -
    -

    todos

    - -
    - -
    - - - - - - diff --git a/dependencies.yml b/dependencies.yml deleted file mode 100644 index b99e04c..0000000 --- a/dependencies.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: 2 -dependencies: -- type: js - path: ./ - settings: - # set the NODE_ENV env variable - # default: development - node_env: production - - # by default we'll collect the package.json versions under the "latest" dist-tag (default npm behavior) - # if you want to follow a specific dist-tag (like "next" or "unstable"), then you - # can specify that here by the package name - # default: none - dist_tags: - semantic-release: next - - # github options - github_labels: # list of label names - - dependencies - - github_assignees: # list of usernames - - djones - - related_pr_behavior: close - diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 8063587..0000000 --- a/index.d.ts +++ /dev/null @@ -1,21 +0,0 @@ - -interface SnapshotOptions { - widths?: number[], - percyCSS?: string, - requestHeaders?: { (key: string): string }, - minHeight?: number, - enableJavaScript?: boolean, -} - -declare namespace Cypress { - interface Chainable { - /** - * Take a snapshot in Percy - * @see https://github.com/percy/percy-cypress - * @example - * cy.percySnapshot('home page') - * cy.percySnapshot('about page', {widths: [1280, 1960]}) - */ - percySnapshot(name?: string, options?: SnapshotOptions): Chainable - } -} diff --git a/index.js b/index.js new file mode 100644 index 0000000..279e838 --- /dev/null +++ b/index.js @@ -0,0 +1,68 @@ +const utils = require('@percy/sdk-utils'); + +// Collect client and environment information +const sdkPkg = require('./package.json'); +const CLIENT_INFO = `${sdkPkg.name}/${sdkPkg.version}`; +const ENV_INFO = `cypress/${Cypress.version}`; + +// Maybe set the CLI API address from the environment +utils.percy.address = Cypress.env('PERCY_SERVER_ADDRESS'); + +// Use Cypress's http:request backend task +utils.request.fetch = async function fetch(url, options) { + options = { url, retryOnNetworkFailure: false, ...options }; + return Cypress.backend('http:request', options); +}; + +// Create Cypress log messages +function cylog(message, meta) { + Cypress.log({ + name: 'percySnapshot', + displayName: 'percy', + consoleProps: () => meta, + message + }); +} + +// Take a DOM snapshot and post it to the snapshot endpoint +Cypress.Commands.add('percySnapshot', (name, options) => { + let log = utils.logger('cypress'); + + // Default name to test title + name = name || cy.state('runnable').fullTitle(); + + cy.then(async () => { + // Check if Percy is enabled + if (!await utils.isPercyEnabled()) { + return cylog('Not running', { name }); + } + + // Inject @percy/dom + if (!window.PercyDOM) { + // eslint-disable-next-line no-eval + eval(await utils.fetchPercyDOM()); + } + + // Serialize and capture the DOM + return cy.document({ log: false }).then(dom => { + let domSnapshot = window.PercyDOM.serialize({ ...options, dom }); + + // Post the DOM snapshot to Percy + return utils.postSnapshot({ + ...options, + environmentInfo: ENV_INFO, + clientInfo: CLIENT_INFO, + domSnapshot, + url: dom.URL, + name + }).then(() => { + // Log the snapshot name on success + cylog(name, { name }); + }).catch(error => { + // Handle errors + log.error(`Could not take DOM snapshot "${name}"`); + log.error(error); + }); + }); + }); +}); diff --git a/lib/environment.ts b/lib/environment.ts deleted file mode 100644 index 999c7d8..0000000 --- a/lib/environment.ts +++ /dev/null @@ -1,19 +0,0 @@ -declare var Cypress: any - -export function clientInfo(): string { - const version: string = require('../package.json').version - const name: string = require('../package.json').name - return `${name}/${version}` -} - -export function environmentInfo(): string { - return `cypress/${_cypressVersion()}` -} - -function _cypressVersion(): string { - try { - return Cypress.version - } catch { - return 'unknown' - } -} diff --git a/lib/index.ts b/lib/index.ts deleted file mode 100644 index 53225a2..0000000 --- a/lib/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {clientInfo, environmentInfo} from './environment' -import PercyAgent from '@percy/agent' - -declare const Cypress: any -declare const cy: any - -Cypress.Commands.add('percySnapshot', (name: string, options: any = {}) => { - cy.task('percyHealthCheck').then((percyIsRunning: boolean) => { - if (percyIsRunning) { - const percyAgentClient = new PercyAgent({ - handleAgentCommunication: false, - domTransformation: options.domTransformation - }) - - name = name || cy.state('runnable').fullTitle() - - cy.document().then((doc: Document) => { - options.document = doc - const domSnapshot = percyAgentClient.snapshot(name, options) - return cy.request({ - method: 'POST', - url: `http://localhost:${percyAgentClient.port}/percy/snapshot`, - failOnStatusCode: false, - body: { - name, - url: doc.URL, - enableJavaScript: options.enableJavaScript, - widths: options.widths, - minHeight: options.minHeight, - clientInfo: clientInfo(), - percyCSS: options.percyCSS, - requestHeaders: options.requestHeaders, - environmentInfo: environmentInfo(), - domSnapshot - } - }) - }) - } - }) -}) diff --git a/package.json b/package.json index 75f09fe..0c302bb 100644 --- a/package.json +++ b/package.json @@ -1,64 +1,47 @@ { "name": "@percy/cypress", - "version": "2.3.4", - "description": "Cypress client library for visual regression testing with Percy (https://percy.io).", - "main": "dist/index.js", - "types": "index.d.ts", - "scripts": { - "pretest": "yarn build", - "test": "percy exec -- node run-tests.js", - "test:debug": "LOG_LEVEL=debug yarn test", - "clean": "rm -rf dist/", - "build": "yarn clean && tsc", - "prepublish": "yarn build", - "lint": "tslint -p . -t stylish --fix", - "cypress:open": "cypress open" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/percy/percy-cypress.git" - }, + "description": "Cypress client library for visual testing with Percy", + "version": "3.0.0-beta.0", + "license": "MIT", + "author": "Perceptual Inc.", + "repository": "https://github.com/percy/percy-cypress", "keywords": [ - "cypress" + "cypress", + "percy", + "visual testing" ], - "engines": { - "node": ">=10.0.0" - }, - "author": "Perceptual Inc.", - "license": "MIT", - "bugs": { - "url": "https://github.com/percy/percy-cypress/issues" - }, - "homepage": "https://github.com/percy/percy-cypress#readme", - "devDependencies": { - "@oclif/tslint": "^3.1.0", - "@semantic-release/changelog": "^5.0.0", - "@semantic-release/git": "^9.0.0", - "cypress": "^6.0.1", - "http-server": "^0.12.0", - "semantic-release": "^17.0.4", - "tslint": "^6.0.0", - "typescript": "^4.0.2" + "main": "index.js", + "types": "types/index.d.ts", + "files": [ + "index.js", + "types/index.d.ts" + ], + "scripts": { + "lint": "eslint --ignore-path .gitignore .", + "test": "node ./node_modules/@percy/sdk-utils/test/server exec -- cypress run", + "coverage": "nyc report --check-coverage", + "test:coverage": "yarn test && yarn coverage", + "test:types": "tsd" }, "dependencies": { - "@percy/agent": "~0", - "axios": "^0.21.0" + "@percy/sdk-utils": "^1.0.0-beta.44" }, "peerDependencies": { - "cypress": "^3 || ^4 || ^5 || ^6" + "cypress": ">=3" }, - "release": { - "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", - [ - "@semantic-release/changelog", - { - "changelogFile": "CHANGELOG.md" - } - ], - "@semantic-release/npm", - "@semantic-release/git" - ] + "devDependencies": { + "@cypress/code-coverage": "^3.8.1", + "@percy/core": "^1.0.0-beta.44", + "babel-plugin-istanbul": "^6.0.0", + "cypress": "^6.8.0", + "eslint": "^7.22.0", + "eslint-config-standard": "^16.0.2", + "eslint-plugin-cypress": "^2.11.2", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^4.3.1", + "eslint-plugin-standard": "^5.0.0", + "nyc": "^15.1.0", + "tsd": "^0.14.0" } } diff --git a/run-tests.js b/run-tests.js deleted file mode 100644 index 36c7238..0000000 --- a/run-tests.js +++ /dev/null @@ -1,32 +0,0 @@ -const httpServer = require('http-server'); -const port = process.env.PORT_NUMBER || 8000; -const spawn = require('child_process').spawn; -const platform = require('os').platform(); - -// We need to change the command path based on the platform they're using -const cmd = /^win/.test(platform) - ? `${process.cwd()}\\node_modules\\.bin\\cypress.cmd` - : `cypress`; - -const server = httpServer.createServer({ root: './cypress/testapp' }) - -server.listen(port) -console.log(`[run-tests] Server is listening on http://localhost:${port}`) - -const tests = spawn(cmd, ['run'], { - stdio: "inherit", - windowsVerbatimArguments: true, -}); - - // Propagate non-zero exit code from test process. -tests.on('exit', code => { - if (code !== 0) { - console.log(`[run-tests] Tests exited with code ${code}. `) - process.exit(code) - } -}); - -tests.on('close', () => { - console.log(`[run-tests] Tests completed! Closing server http://localhost:${port}`) - server.close() -}); diff --git a/task.js b/task.js deleted file mode 100644 index 9ba50ad..0000000 --- a/task.js +++ /dev/null @@ -1,9 +0,0 @@ -const Axios = require("axios"); - -module.exports = { - percyHealthCheck() { - return Axios.get("http://localhost:5338/percy/healthcheck") - .then(() => true) - .catch(() => false); - } -}; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 6febcd0..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "compilerOptions": { - /* Basic Options */ - "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": ["es6", "dom"], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./dist", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - } -} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index cae0bd0..0000000 --- a/tslint.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@oclif/tslint", - "rules": { - "no-console": false, - "radix": false, - "ordered-imports": false, - "no-string-based-set-timeout": false, - "no-string-throw": false - } -} diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..c97bb80 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,14 @@ +/// +/// +import { SnapshotOptions } from '@percy/core'; + +declare global { + namespace Cypress { + interface Chainable { + percySnapshot( + name?: string, + options?: SnapshotOptions + ): Chainable + } + } +} diff --git a/types/index.test-d.ts b/types/index.test-d.ts new file mode 100644 index 0000000..1b25276 --- /dev/null +++ b/types/index.test-d.ts @@ -0,0 +1,8 @@ +/// +import { expectType, expectError } from 'tsd'; + +expectType>(cy.percySnapshot()); +expectType>(cy.percySnapshot('Snapshot name')); +expectType>(cy.percySnapshot('Snapshot name', { widths: [1000] })); + +expectError(cy.percySnapshot('Snapshot name', { foo: 'bar' }));