Be safe & secure:
diff --git a/common/components/ui/Modal/ModalBody.tsx b/common/components/ui/Modal/ModalBody.tsx
index 1f4d2fb8..03b6ef29 100644
--- a/common/components/ui/Modal/ModalBody.tsx
+++ b/common/components/ui/Modal/ModalBody.tsx
@@ -50,6 +50,7 @@ export default class ModalBody extends React.Component {
className="Modal"
style={modalStyle}
role="dialog"
+ data-cy="Modal"
aria-labelledby="Modal-header-title"
ref={div => {
this.modal = div as HTMLElement;
@@ -93,6 +94,7 @@ export default class ModalBody extends React.Component {
onClick={btn.onClick}
key={idx}
disabled={disableButtons || btn.disabled}
+ data-cy="Modal-Button"
>
{btn.text}
diff --git a/common/containers/OnboardModal/components/OnboardSlide.tsx b/common/containers/OnboardModal/components/OnboardSlide.tsx
index 202abc86..efb299d5 100644
--- a/common/containers/OnboardModal/components/OnboardSlide.tsx
+++ b/common/containers/OnboardModal/components/OnboardSlide.tsx
@@ -13,7 +13,7 @@ class OnboardSlide extends React.Component {
public render() {
const { header, subheader, content, image, imageSide } = this.props;
return (
-
+
{header}
{subheader &&
{subheader}
}
diff --git a/common/containers/OnboardModal/index.tsx b/common/containers/OnboardModal/index.tsx
index b415ad7f..0313ba18 100644
--- a/common/containers/OnboardModal/index.tsx
+++ b/common/containers/OnboardModal/index.tsx
@@ -119,7 +119,7 @@ class OnboardModal extends React.Component
{
const steps = new Array(NUMBER_OF_SLIDES).fill({});
return (
-
+
{
const { isUnavailableOffline, children, isOffline, latestBlock } = this.props;
return (
-
+
(
diff --git a/cypress.json b/cypress.json
new file mode 100644
index 00000000..30bfed6d
--- /dev/null
+++ b/cypress.json
@@ -0,0 +1 @@
+{ "chromeWebSecurity": false, "baseUrl": "https://localhost:3443" }
diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json
new file mode 100644
index 00000000..da18d935
--- /dev/null
+++ b/cypress/fixtures/example.json
@@ -0,0 +1,5 @@
+{
+ "name": "Using fixtures to represent data",
+ "email": "hello@cypress.io",
+ "body": "Fixtures are a great way to mock data for responses to routes"
+}
\ No newline at end of file
diff --git a/cypress/integration/example_spec.js b/cypress/integration/example_spec.js
new file mode 100644
index 00000000..562d88de
--- /dev/null
+++ b/cypress/integration/example_spec.js
@@ -0,0 +1,1497 @@
+//
+// **** Kitchen Sink Tests ****
+//
+// This app was developed to demonstrate
+// how to write tests in Cypress utilizing
+// all of the available commands
+//
+// Feel free to modify this spec in your
+// own application as a jumping off point
+
+// Please read our "Introduction to Cypress"
+// https://on.cypress.io/introduction-to-cypress
+
+describe('Kitchen Sink', function () {
+ it('.should() - assert that is correct', function () {
+ // https://on.cypress.io/visit
+ cy.visit('https://example.cypress.io')
+
+ // Here we've made our first assertion using a '.should()' command.
+ // An assertion is comprised of a chainer, subject, and optional value.
+
+ // https://on.cypress.io/should
+ // https://on.cypress.io/and
+
+ // https://on.cypress.io/title
+ cy.title().should('include', 'Kitchen Sink')
+ // ↲ ↲ ↲
+ // subject chainer value
+ })
+
+ context('Querying', function () {
+ beforeEach(function () {
+ // Visiting our app before each test removes any state build up from
+ // previous tests. Visiting acts as if we closed a tab and opened a fresh one
+ cy.visit('https://example.cypress.io/commands/querying')
+ })
+
+ // Let's query for some DOM elements and make assertions
+ // The most commonly used query is 'cy.get()', you can
+ // think of this like the '$' in jQuery
+
+ it('cy.get() - query DOM elements', function () {
+ // https://on.cypress.io/get
+
+ // Get DOM elements by id
+ cy.get('#query-btn').should('contain', 'Button')
+
+ // Get DOM elements by class
+ cy.get('.query-btn').should('contain', 'Button')
+
+ cy.get('#querying .well>button:first').should('contain', 'Button')
+ // ↲
+ // Use CSS selectors just like jQuery
+ })
+
+ it('cy.contains() - query DOM elements with matching content', function () {
+ // https://on.cypress.io/contains
+ cy.get('.query-list')
+ .contains('bananas').should('have.class', 'third')
+
+ // we can pass a regexp to `.contains()`
+ cy.get('.query-list')
+ .contains(/^b\w+/).should('have.class', 'third')
+
+ cy.get('.query-list')
+ .contains('apples').should('have.class', 'first')
+
+ // passing a selector to contains will yield the selector containing the text
+ cy.get('#querying')
+ .contains('ul', 'oranges').should('have.class', 'query-list')
+
+ // `.contains()` will favor input[type='submit'],
+ // button, a, and label over deeper elements inside them
+ // this will not yield the inside the button,
+ // but the itself
+ cy.get('.query-button')
+ .contains('Save Form').should('have.class', 'btn')
+ })
+
+ it('.within() - query DOM elements within a specific element', function () {
+ // https://on.cypress.io/within
+ cy.get('.query-form').within(function () {
+ cy.get('input:first').should('have.attr', 'placeholder', 'Email')
+ cy.get('input:last').should('have.attr', 'placeholder', 'Password')
+ })
+ })
+
+ it('cy.root() - query the root DOM element', function () {
+ // https://on.cypress.io/root
+ // By default, root is the document
+ cy.root().should('match', 'html')
+
+ cy.get('.query-ul').within(function () {
+ // In this within, the root is now the ul DOM element
+ cy.root().should('have.class', 'query-ul')
+ })
+ })
+ })
+
+ context('Traversal', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/traversal')
+ })
+
+ // Let's query for some DOM elements and make assertions
+
+ it('.children() - get child DOM elements', function () {
+ // https://on.cypress.io/children
+ cy.get('.traversal-breadcrumb').children('.active')
+ .should('contain', 'Data')
+ })
+
+ it('.closest() - get closest ancestor DOM element', function () {
+ // https://on.cypress.io/closest
+ cy.get('.traversal-badge').closest('ul')
+ .should('have.class', 'list-group')
+ })
+
+ it('.eq() - get a DOM element at a specific index', function () {
+ // https://on.cypress.io/eq
+ cy.get('.traversal-list>li').eq(1).should('contain', 'siamese')
+ })
+
+ it('.filter() - get DOM elements that match the selector', function () {
+ // https://on.cypress.io/filter
+ cy.get('.traversal-nav>li').filter('.active').should('contain', 'About')
+ })
+
+ it('.find() - get descendant DOM elements of the selector', function () {
+ // https://on.cypress.io/find
+ cy.get('.traversal-pagination').find('li').find('a')
+ .should('have.length', 7)
+ })
+
+ it('.first() - get first DOM element', function () {
+ // https://on.cypress.io/first
+ cy.get('.traversal-table td').first().should('contain', '1')
+ })
+
+ it('.last() - get last DOM element', function () {
+ // https://on.cypress.io/last
+ cy.get('.traversal-buttons .btn').last().should('contain', 'Submit')
+ })
+
+ it('.next() - get next sibling DOM element', function () {
+ // https://on.cypress.io/next
+ cy.get('.traversal-ul').contains('apples').next().should('contain', 'oranges')
+ })
+
+ it('.nextAll() - get all next sibling DOM elements', function () {
+ // https://on.cypress.io/nextall
+ cy.get('.traversal-next-all').contains('oranges')
+ .nextAll().should('have.length', 3)
+ })
+
+ it('.nextUntil() - get next sibling DOM elements until next el', function () {
+ // https://on.cypress.io/nextuntil
+ cy.get('#veggies').nextUntil('#nuts').should('have.length', 3)
+ })
+
+ it('.not() - remove DOM elements from set of DOM elements', function () {
+ // https://on.cypress.io/not
+ cy.get('.traversal-disabled .btn').not('[disabled]').should('not.contain', 'Disabled')
+ })
+
+ it('.parent() - get parent DOM element from DOM elements', function () {
+ // https://on.cypress.io/parent
+ cy.get('.traversal-mark').parent().should('contain', 'Morbi leo risus')
+ })
+
+ it('.parents() - get parent DOM elements from DOM elements', function () {
+ // https://on.cypress.io/parents
+ cy.get('.traversal-cite').parents().should('match', 'blockquote')
+ })
+
+ it('.parentsUntil() - get parent DOM elements from DOM elements until el', function () {
+ // https://on.cypress.io/parentsuntil
+ cy.get('.clothes-nav').find('.active').parentsUntil('.clothes-nav')
+ .should('have.length', 2)
+ })
+
+ it('.prev() - get previous sibling DOM element', function () {
+ // https://on.cypress.io/prev
+ cy.get('.birds').find('.active').prev().should('contain', 'Lorikeets')
+ })
+
+ it('.prevAll() - get all previous sibling DOM elements', function () {
+ // https://on.cypress.io/prevAll
+ cy.get('.fruits-list').find('.third').prevAll().should('have.length', 2)
+ })
+
+ it('.prevUntil() - get all previous sibling DOM elements until el', function () {
+ // https://on.cypress.io/prevUntil
+ cy.get('.foods-list').find('#nuts').prevUntil('#veggies')
+ })
+
+ it('.siblings() - get all sibling DOM elements', function () {
+ // https://on.cypress.io/siblings
+ cy.get('.traversal-pills .active').siblings().should('have.length', 2)
+ })
+ })
+
+ context('Actions', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/actions')
+ })
+
+ // Let's perform some actions on DOM elements
+ // https://on.cypress.io/interacting-with-elements
+
+ it('.type() - type into a DOM element', function () {
+ // https://on.cypress.io/type
+ cy.get('.action-email')
+ .type('fake@email.com').should('have.value', 'fake@email.com')
+
+ // .type() with special character sequences
+ .type('{leftarrow}{rightarrow}{uparrow}{downarrow}')
+ .type('{del}{selectall}{backspace}')
+
+ // .type() with key modifiers
+ .type('{alt}{option}') //these are equivalent
+ .type('{ctrl}{control}') //these are equivalent
+ .type('{meta}{command}{cmd}') //these are equivalent
+ .type('{shift}')
+
+ // Delay each keypress by 0.1 sec
+ .type('slow.typing@email.com', { delay: 100 })
+ .should('have.value', 'slow.typing@email.com')
+
+ cy.get('.action-disabled')
+ // Ignore error checking prior to type
+ // like whether the input is visible or disabled
+ .type('disabled error checking', { force: true })
+ .should('have.value', 'disabled error checking')
+ })
+
+ it('.focus() - focus on a DOM element', function () {
+ // https://on.cypress.io/focus
+ cy.get('.action-focus').focus()
+ .should('have.class', 'focus')
+ .prev().should('have.attr', 'style', 'color: orange;')
+ })
+
+ it('.blur() - blur off a DOM element', function () {
+ // https://on.cypress.io/blur
+ cy.get('.action-blur').type('I\'m about to blur').blur()
+ .should('have.class', 'error')
+ .prev().should('have.attr', 'style', 'color: red;')
+ })
+
+ it('.clear() - clears an input or textarea element', function () {
+ // https://on.cypress.io/clear
+ cy.get('.action-clear').type('We are going to clear this text')
+ .should('have.value', 'We are going to clear this text')
+ .clear()
+ .should('have.value', '')
+ })
+
+ it('.submit() - submit a form', function () {
+ // https://on.cypress.io/submit
+ cy.get('.action-form')
+ .find('[type="text"]').type('HALFOFF')
+ cy.get('.action-form').submit()
+ .next().should('contain', 'Your form has been submitted!')
+ })
+
+ it('.click() - click on a DOM element', function () {
+ // https://on.cypress.io/click
+ cy.get('.action-btn').click()
+
+ // You can click on 9 specific positions of an element:
+ // -----------------------------------
+ // | topLeft top topRight |
+ // | |
+ // | |
+ // | |
+ // | left center right |
+ // | |
+ // | |
+ // | |
+ // | bottomLeft bottom bottomRight |
+ // -----------------------------------
+
+ // clicking in the center of the element is the default
+ cy.get('#action-canvas').click()
+
+ cy.get('#action-canvas').click('topLeft')
+ cy.get('#action-canvas').click('top')
+ cy.get('#action-canvas').click('topRight')
+ cy.get('#action-canvas').click('left')
+ cy.get('#action-canvas').click('right')
+ cy.get('#action-canvas').click('bottomLeft')
+ cy.get('#action-canvas').click('bottom')
+ cy.get('#action-canvas').click('bottomRight')
+
+ // .click() accepts an x and y coordinate
+ // that controls where the click occurs :)
+
+ cy.get('#action-canvas')
+ .click(80, 75) // click 80px on x coord and 75px on y coord
+ .click(170, 75)
+ .click(80, 165)
+ .click(100, 185)
+ .click(125, 190)
+ .click(150, 185)
+ .click(170, 165)
+
+ // click multiple elements by passing multiple: true
+ cy.get('.action-labels>.label').click({ multiple: true })
+
+ // Ignore error checking prior to clicking
+ // like whether the element is visible, clickable or disabled
+ // this button below is covered by another element.
+ cy.get('.action-opacity>.btn').click({ force: true })
+ })
+
+ it('.dblclick() - double click on a DOM element', function () {
+ // Our app has a listener on 'dblclick' event in our 'scripts.js'
+ // that hides the div and shows an input on double click
+
+ // https://on.cypress.io/dblclick
+ cy.get('.action-div').dblclick().should('not.be.visible')
+ cy.get('.action-input-hidden').should('be.visible')
+ })
+
+ it('cy.check() - check a checkbox or radio element', function () {
+ // By default, .check() will check all
+ // matching checkbox or radio elements in succession, one after another
+
+ // https://on.cypress.io/check
+ cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]')
+ .check().should('be.checked')
+
+ cy.get('.action-radios [type="radio"]').not('[disabled]')
+ .check().should('be.checked')
+
+ // .check() accepts a value argument
+ // that checks only checkboxes or radios
+ // with matching values
+ cy.get('.action-radios [type="radio"]').check('radio1').should('be.checked')
+
+ // .check() accepts an array of values
+ // that checks only checkboxes or radios
+ // with matching values
+ cy.get('.action-multiple-checkboxes [type="checkbox"]')
+ .check(['checkbox1', 'checkbox2']).should('be.checked')
+
+ // Ignore error checking prior to checking
+ // like whether the element is visible, clickable or disabled
+ // this checkbox below is disabled.
+ cy.get('.action-checkboxes [disabled]')
+ .check({ force: true }).should('be.checked')
+
+ cy.get('.action-radios [type="radio"]')
+ .check('radio3', { force: true }).should('be.checked')
+ })
+
+ it('.uncheck() - uncheck a checkbox element', function () {
+ // By default, .uncheck() will uncheck all matching
+ // checkbox elements in succession, one after another
+
+ // https://on.cypress.io/uncheck
+ cy.get('.action-check [type="checkbox"]')
+ .not('[disabled]')
+ .uncheck().should('not.be.checked')
+
+ // .uncheck() accepts a value argument
+ // that unchecks only checkboxes
+ // with matching values
+ cy.get('.action-check [type="checkbox"]')
+ .check('checkbox1')
+ .uncheck('checkbox1').should('not.be.checked')
+
+ // .uncheck() accepts an array of values
+ // that unchecks only checkboxes or radios
+ // with matching values
+ cy.get('.action-check [type="checkbox"]')
+ .check(['checkbox1', 'checkbox3'])
+ .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked')
+
+ // Ignore error checking prior to unchecking
+ // like whether the element is visible, clickable or disabled
+ // this checkbox below is disabled.
+ cy.get('.action-check [disabled]')
+ .uncheck({ force: true }).should('not.be.checked')
+ })
+
+ it('.select() - select an option in a element', function () {
+ // https://on.cypress.io/select
+
+ // Select option with matching text content
+ cy.get('.action-select').select('apples')
+
+ // Select option with matching value
+ cy.get('.action-select').select('fr-bananas')
+
+ // Select options with matching text content
+ cy.get('.action-select-multiple')
+ .select(['apples', 'oranges', 'bananas'])
+
+ // Select options with matching values
+ cy.get('.action-select-multiple')
+ .select(['fr-apples', 'fr-oranges', 'fr-bananas'])
+ })
+
+ it('.scrollIntoView() - scroll an element into view', function () {
+ // https://on.cypress.io/scrollintoview
+
+ // normally all of these buttons are hidden, because they're not within
+ // the viewable area of their parent (we need to scroll to see them)
+ cy.get('#scroll-horizontal button')
+ .should('not.be.visible')
+
+ // scroll the button into view, as if the user had scrolled
+ cy.get('#scroll-horizontal button').scrollIntoView()
+ .should('be.visible')
+
+ cy.get('#scroll-vertical button')
+ .should('not.be.visible')
+
+ // Cypress handles the scroll direction needed
+ cy.get('#scroll-vertical button').scrollIntoView()
+ .should('be.visible')
+
+ cy.get('#scroll-both button')
+ .should('not.be.visible')
+
+ // Cypress knows to scroll to the right and down
+ cy.get('#scroll-both button').scrollIntoView()
+ .should('be.visible')
+ })
+
+ it('cy.scrollTo() - scroll the window or element to a position', function () {
+
+ // https://on.cypress.io/scrollTo
+
+ // You can scroll to 9 specific positions of an element:
+ // -----------------------------------
+ // | topLeft top topRight |
+ // | |
+ // | |
+ // | |
+ // | left center right |
+ // | |
+ // | |
+ // | |
+ // | bottomLeft bottom bottomRight |
+ // -----------------------------------
+
+ // if you chain .scrollTo() off of cy, we will
+ // scroll the entire window
+ cy.scrollTo('bottom')
+
+ cy.get('#scrollable-horizontal').scrollTo('right')
+
+ // or you can scroll to a specific coordinate:
+ // (x axis, y axis) in pixels
+ cy.get('#scrollable-vertical').scrollTo(250, 250)
+
+ // or you can scroll to a specific percentage
+ // of the (width, height) of the element
+ cy.get('#scrollable-both').scrollTo('75%', '25%')
+
+ // control the easing of the scroll (default is 'swing')
+ cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' })
+
+ // control the duration of the scroll (in ms)
+ cy.get('#scrollable-both').scrollTo('center', { duration: 2000 })
+ })
+
+ it('.trigger() - trigger an event on a DOM element', function () {
+ // To interact with a range input (slider), we need to set its value and
+ // then trigger the appropriate event to signal it has changed
+
+ // Here, we invoke jQuery's val() method to set the value
+ // and trigger the 'change' event
+
+ // Note that some implementations may rely on the 'input' event,
+ // which is fired as a user moves the slider, but is not supported
+ // by some browsers
+
+ // https://on.cypress.io/trigger
+ cy.get('.trigger-input-range')
+ .invoke('val', 25)
+ .trigger('change')
+ .get('input[type=range]').siblings('p')
+ .should('have.text', '25')
+
+ // See our example recipes for more examples of using trigger
+ // https://on.cypress.io/examples
+ })
+ })
+
+ context('Window', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/window')
+ })
+
+ it('cy.window() - get the global window object', function () {
+ // https://on.cypress.io/window
+ cy.window().should('have.property', 'top')
+ })
+
+ it('cy.document() - get the document object', function () {
+ // https://on.cypress.io/document
+ cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
+ })
+
+ it('cy.title() - get the title', function () {
+ // https://on.cypress.io/title
+ cy.title().should('include', 'Kitchen Sink')
+ })
+ })
+
+ context('Viewport', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/viewport')
+ })
+
+ it('cy.viewport() - set the viewport size and dimension', function () {
+
+ cy.get('#navbar').should('be.visible')
+
+ // https://on.cypress.io/viewport
+ cy.viewport(320, 480)
+
+ // the navbar should have collapse since our screen is smaller
+ cy.get('#navbar').should('not.be.visible')
+ cy.get('.navbar-toggle').should('be.visible').click()
+ cy.get('.nav').find('a').should('be.visible')
+
+ // lets see what our app looks like on a super large screen
+ cy.viewport(2999, 2999)
+
+ // cy.viewport() accepts a set of preset sizes
+ // to easily set the screen to a device's width and height
+
+ // We added a cy.wait() between each viewport change so you can see
+ // the change otherwise it's a little too fast to see :)
+
+ cy.viewport('macbook-15')
+ cy.wait(200)
+ cy.viewport('macbook-13')
+ cy.wait(200)
+ cy.viewport('macbook-11')
+ cy.wait(200)
+ cy.viewport('ipad-2')
+ cy.wait(200)
+ cy.viewport('ipad-mini')
+ cy.wait(200)
+ cy.viewport('iphone-6+')
+ cy.wait(200)
+ cy.viewport('iphone-6')
+ cy.wait(200)
+ cy.viewport('iphone-5')
+ cy.wait(200)
+ cy.viewport('iphone-4')
+ cy.wait(200)
+ cy.viewport('iphone-3')
+ cy.wait(200)
+
+ // cy.viewport() accepts an orientation for all presets
+ // the default orientation is 'portrait'
+ cy.viewport('ipad-2', 'portrait')
+ cy.wait(200)
+ cy.viewport('iphone-4', 'landscape')
+ cy.wait(200)
+
+ // The viewport will be reset back to the default dimensions
+ // in between tests (the default is set in cypress.json)
+ })
+ })
+
+ context('Location', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/location')
+ })
+
+ // We look at the url to make assertions
+ // about the page's state
+
+ it('cy.hash() - get the current URL hash', function () {
+ // https://on.cypress.io/hash
+ cy.hash().should('be.empty')
+ })
+
+ it('cy.location() - get window.location', function () {
+ // https://on.cypress.io/location
+ cy.location().should(function (location) {
+ expect(location.hash).to.be.empty
+ expect(location.href).to.eq('https://example.cypress.io/commands/location')
+ expect(location.host).to.eq('example.cypress.io')
+ expect(location.hostname).to.eq('example.cypress.io')
+ expect(location.origin).to.eq('https://example.cypress.io')
+ expect(location.pathname).to.eq('/commands/location')
+ expect(location.port).to.eq('')
+ expect(location.protocol).to.eq('https:')
+ expect(location.search).to.be.empty
+ })
+ })
+
+ it('cy.url() - get the current URL', function () {
+ // https://on.cypress.io/url
+ cy.url().should('eq', 'https://example.cypress.io/commands/location')
+ })
+ })
+
+ context('Navigation', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io')
+ cy.get('.navbar-nav').contains('Commands').click()
+ cy.get('.dropdown-menu').contains('Navigation').click()
+ })
+
+ it('cy.go() - go back or forward in the browser\'s history', function () {
+ cy.location('pathname').should('include', 'navigation')
+
+ // https://on.cypress.io/go
+ cy.go('back')
+ cy.location('pathname').should('not.include', 'navigation')
+
+ cy.go('forward')
+ cy.location('pathname').should('include', 'navigation')
+
+ // equivalent to clicking back
+ cy.go(-1)
+ cy.location('pathname').should('not.include', 'navigation')
+
+ // equivalent to clicking forward
+ cy.go(1)
+ cy.location('pathname').should('include', 'navigation')
+ })
+
+ it('cy.reload() - reload the page', function () {
+ // https://on.cypress.io/reload
+ cy.reload()
+
+ // reload the page without using the cache
+ cy.reload(true)
+ })
+
+ it('cy.visit() - visit a remote url', function () {
+ // Visit any sub-domain of your current domain
+ // https://on.cypress.io/visit
+
+ // Pass options to the visit
+ cy.visit('https://example.cypress.io/commands/navigation', {
+ timeout: 50000, // increase total time for the visit to resolve
+ onBeforeLoad (contentWindow) {
+ // contentWindow is the remote page's window object
+ },
+ onLoad (contentWindow) {
+ // contentWindow is the remote page's window object
+ },
+ })
+ })
+ })
+
+ context('Assertions', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/assertions')
+ })
+
+ describe('Implicit Assertions', function () {
+
+ it('.should() - make an assertion about the current subject', function () {
+ // https://on.cypress.io/should
+ cy.get('.assertion-table')
+ .find('tbody tr:last').should('have.class', 'success')
+ })
+
+ it('.and() - chain multiple assertions together', function () {
+ // https://on.cypress.io/and
+ cy.get('.assertions-link')
+ .should('have.class', 'active')
+ .and('have.attr', 'href')
+ .and('include', 'cypress.io')
+ })
+ })
+
+ describe('Explicit Assertions', function () {
+ // https://on.cypress.io/assertions
+ it('expect - assert shape of an object', function () {
+ const person = {
+ name: 'Joe',
+ age: 20,
+ }
+ expect(person).to.have.all.keys('name', 'age')
+ })
+
+ it('expect - make an assertion about a specified subject', function () {
+ // We can use Chai's BDD style assertions
+ expect(true).to.be.true
+
+ // Pass a function to should that can have any number
+ // of explicit assertions within it.
+ cy.get('.assertions-p').find('p')
+ .should(function ($p) {
+ // return an array of texts from all of the p's
+ let texts = $p.map(function (i, el) {
+ // https://on.cypress.io/$
+ return Cypress.$(el).text()
+ })
+
+ // jquery map returns jquery object
+ // and .get() convert this to simple array
+ texts = texts.get()
+
+ // array should have length of 3
+ expect(texts).to.have.length(3)
+
+ // set this specific subject
+ expect(texts).to.deep.eq([
+ 'Some text from first p',
+ 'More text from second p',
+ 'And even more text from third p',
+ ])
+ })
+ })
+ })
+ })
+
+ context('Misc', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/misc')
+ })
+
+ it('.end() - end the command chain', function () {
+ // cy.end is useful when you want to end a chain of commands
+ // and force Cypress to re-query from the root element
+
+ // https://on.cypress.io/end
+ cy.get('.misc-table').within(function () {
+ // ends the current chain and yields null
+ cy.contains('Cheryl').click().end()
+
+ // queries the entire table again
+ cy.contains('Charles').click()
+ })
+ })
+
+ it('cy.exec() - execute a system command', function () {
+ // cy.exec allows you to execute a system command.
+ // so you can take actions necessary for your test,
+ // but outside the scope of Cypress.
+
+ // https://on.cypress.io/exec
+ cy.exec('echo Jane Lane')
+ .its('stdout').should('contain', 'Jane Lane')
+
+ // we can use Cypress.platform string to
+ // select appropriate command
+ // https://on.cypress/io/platform
+ cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`)
+
+ if (Cypress.platform === 'win32') {
+ cy.exec('print cypress.json')
+ .its('stderr').should('be.empty')
+ } else {
+ cy.exec('cat cypress.json')
+ .its('stderr').should('be.empty')
+
+ cy.exec('pwd')
+ .its('code').should('eq', 0)
+ }
+ })
+
+ it('cy.focused() - get the DOM element that has focus', function () {
+ // https://on.cypress.io/focused
+ cy.get('.misc-form').find('#name').click()
+ cy.focused().should('have.id', 'name')
+
+ cy.get('.misc-form').find('#description').click()
+ cy.focused().should('have.id', 'description')
+ })
+
+ it('cy.screenshot() - take a screenshot', function () {
+ // https://on.cypress.io/screenshot
+ cy.screenshot('my-image')
+ })
+
+ it('cy.wrap() - wrap an object', function () {
+ // https://on.cypress.io/wrap
+ cy.wrap({ foo: 'bar' })
+ .should('have.property', 'foo')
+ .and('include', 'bar')
+ })
+ })
+
+ context('Connectors', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/connectors')
+ })
+
+ it('.each() - iterate over an array of elements', function () {
+ // https://on.cypress.io/each
+ cy.get('.connectors-each-ul>li')
+ .each(function ($el, index, $list) {
+ console.log($el, index, $list)
+ })
+ })
+
+ it('.its() - get properties on the current subject', function () {
+ // https://on.cypress.io/its
+ cy.get('.connectors-its-ul>li')
+ // calls the 'length' property yielding that value
+ .its('length')
+ .should('be.gt', 2)
+ })
+
+ it('.invoke() - invoke a function on the current subject', function () {
+ // our div is hidden in our script.js
+ // $('.connectors-div').hide()
+
+ // https://on.cypress.io/invoke
+ cy.get('.connectors-div').should('be.hidden')
+
+ // call the jquery method 'show' on the 'div.container'
+ .invoke('show')
+ .should('be.visible')
+ })
+
+ it('.spread() - spread an array as individual args to callback function', function () {
+ // https://on.cypress.io/spread
+ let arr = ['foo', 'bar', 'baz']
+
+ cy.wrap(arr).spread(function (foo, bar, baz) {
+ expect(foo).to.eq('foo')
+ expect(bar).to.eq('bar')
+ expect(baz).to.eq('baz')
+ })
+ })
+
+ it('.then() - invoke a callback function with the current subject', function () {
+ // https://on.cypress.io/then
+ cy.get('.connectors-list>li').then(function ($lis) {
+ expect($lis).to.have.length(3)
+ expect($lis.eq(0)).to.contain('Walk the dog')
+ expect($lis.eq(1)).to.contain('Feed the cat')
+ expect($lis.eq(2)).to.contain('Write JavaScript')
+ })
+ })
+ })
+
+ context('Aliasing', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/aliasing')
+ })
+
+ // We alias a DOM element for use later
+ // We don't have to traverse to the element
+ // later in our code, we just reference it with @
+
+ it('.as() - alias a route or DOM element for later use', function () {
+ // this is a good use case for an alias,
+ // we don't want to write this long traversal again
+
+ // https://on.cypress.io/as
+ cy.get('.as-table').find('tbody>tr')
+ .first().find('td').first().find('button').as('firstBtn')
+
+ // maybe do some more testing here...
+
+ // when we reference the alias, we place an
+ // @ in front of it's name
+ cy.get('@firstBtn').click()
+
+ cy.get('@firstBtn')
+ .should('have.class', 'btn-success')
+ .and('contain', 'Changed')
+ })
+ })
+
+ context('Waiting', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/waiting')
+ })
+ // BE CAREFUL of adding unnecessary wait times.
+
+ // https://on.cypress.io/wait
+ it('cy.wait() - wait for a specific amount of time', function () {
+ cy.get('.wait-input1').type('Wait 1000ms after typing')
+ cy.wait(1000)
+ cy.get('.wait-input2').type('Wait 1000ms after typing')
+ cy.wait(1000)
+ cy.get('.wait-input3').type('Wait 1000ms after typing')
+ cy.wait(1000)
+ })
+
+ // Waiting for a specific resource to resolve
+ // is covered within the cy.route() test below
+ })
+
+ context('Network Requests', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/network-requests')
+ })
+
+ // Manage AJAX / XHR requests in your app
+
+ it('cy.server() - control behavior of network requests and responses', function () {
+ // https://on.cypress.io/server
+ cy.server().should(function (server) {
+ // the default options on server
+ // you can override any of these options
+ expect(server.delay).to.eq(0)
+ expect(server.method).to.eq('GET')
+ expect(server.status).to.eq(200)
+ expect(server.headers).to.be.null
+ expect(server.response).to.be.null
+ expect(server.onRequest).to.be.undefined
+ expect(server.onResponse).to.be.undefined
+ expect(server.onAbort).to.be.undefined
+
+ // These options control the server behavior
+ // affecting all requests
+
+ // pass false to disable existing route stubs
+ expect(server.enable).to.be.true
+ // forces requests that don't match your routes to 404
+ expect(server.force404).to.be.false
+ // whitelists requests from ever being logged or stubbed
+ expect(server.whitelist).to.be.a('function')
+ })
+
+ cy.server({
+ method: 'POST',
+ delay: 1000,
+ status: 422,
+ response: {},
+ })
+
+ // any route commands will now inherit the above options
+ // from the server. anything we pass specifically
+ // to route will override the defaults though.
+ })
+
+ it('cy.request() - make an XHR request', function () {
+ // https://on.cypress.io/request
+ cy.request('https://jsonplaceholder.typicode.com/comments')
+ .should(function (response) {
+ expect(response.status).to.eq(200)
+ expect(response.body).to.have.length(500)
+ expect(response).to.have.property('headers')
+ expect(response).to.have.property('duration')
+ })
+ })
+
+ it('cy.route() - route responses to matching requests', function () {
+ let message = 'whoa, this comment doesn\'t exist'
+ cy.server()
+
+ // **** GET comments route ****
+
+ // https://on.cypress.io/route
+ cy.route(/comments\/1/).as('getComment')
+
+ // we have code that fetches a comment when
+ // the button is clicked in scripts.js
+ cy.get('.network-btn').click()
+
+ // **** Wait ****
+
+ // Wait for a specific resource to resolve
+ // continuing to the next command
+
+ // https://on.cypress.io/wait
+ cy.wait('@getComment').its('status').should('eq', 200)
+
+ // **** POST comment route ****
+
+ // Specify the route to listen to method 'POST'
+ cy.route('POST', '/comments').as('postComment')
+
+ // we have code that posts a comment when
+ // the button is clicked in scripts.js
+ cy.get('.network-post').click()
+ cy.wait('@postComment')
+
+ // get the route
+ cy.get('@postComment').then(function (xhr) {
+ expect(xhr.requestBody).to.include('email')
+ expect(xhr.requestHeaders).to.have.property('Content-Type')
+ expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()')
+ })
+
+ // **** Stubbed PUT comment route ****
+ cy.route({
+ method: 'PUT',
+ url: /comments\/\d+/,
+ status: 404,
+ response: { error: message },
+ delay: 500,
+ }).as('putComment')
+
+ // we have code that puts a comment when
+ // the button is clicked in scripts.js
+ cy.get('.network-put').click()
+
+ cy.wait('@putComment')
+
+ // our 404 statusCode logic in scripts.js executed
+ cy.get('.network-put-comment').should('contain', message)
+ })
+ })
+
+ context('Files', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/files')
+ })
+ it('cy.fixture() - load a fixture', function () {
+ // Instead of writing a response inline you can
+ // connect a response with a fixture file
+ // located in fixtures folder.
+
+ cy.server()
+
+ // https://on.cypress.io/fixture
+ cy.fixture('example.json').as('comment')
+
+ cy.route(/comments/, '@comment').as('getComment')
+
+ // we have code that gets a comment when
+ // the button is clicked in scripts.js
+ cy.get('.fixture-btn').click()
+
+ cy.wait('@getComment').its('responseBody')
+ .should('have.property', 'name')
+ .and('include', 'Using fixtures to represent data')
+
+ // you can also just write the fixture in the route
+ cy.route(/comments/, 'fixture:example.json').as('getComment')
+
+ // we have code that gets a comment when
+ // the button is clicked in scripts.js
+ cy.get('.fixture-btn').click()
+
+ cy.wait('@getComment').its('responseBody')
+ .should('have.property', 'name')
+ .and('include', 'Using fixtures to represent data')
+
+ // or write fx to represent fixture
+ // by default it assumes it's .json
+ cy.route(/comments/, 'fx:example').as('getComment')
+
+ // we have code that gets a comment when
+ // the button is clicked in scripts.js
+ cy.get('.fixture-btn').click()
+
+ cy.wait('@getComment').its('responseBody')
+ .should('have.property', 'name')
+ .and('include', 'Using fixtures to represent data')
+ })
+
+ it('cy.readFile() - read a files contents', function () {
+ // You can read a file and yield its contents
+ // The filePath is relative to your project's root.
+
+ // https://on.cypress.io/readfile
+ cy.readFile('cypress.json').then(function (json) {
+ expect(json).to.be.an('object')
+ })
+
+ })
+
+ it('cy.writeFile() - write to a file', function () {
+ // You can write to a file with the specified contents
+
+ // Use a response from a request to automatically
+ // generate a fixture file for use later
+ cy.request('https://jsonplaceholder.typicode.com/users')
+ .then(function (response) {
+ // https://on.cypress.io/writefile
+ cy.writeFile('cypress/fixtures/users.json', response.body)
+ })
+ cy.fixture('users').should(function (users) {
+ expect(users[0].name).to.exist
+ })
+
+ // JavaScript arrays and objects are stringified and formatted into text.
+ cy.writeFile('cypress/fixtures/profile.json', {
+ id: 8739,
+ name: 'Jane',
+ email: 'jane@example.com',
+ })
+
+ cy.fixture('profile').should(function (profile) {
+ expect(profile.name).to.eq('Jane')
+ })
+ })
+ })
+
+ context('Local Storage', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/commands/local-storage')
+ })
+ // Although local storage is automatically cleared
+ // to maintain a clean state in between tests
+ // sometimes we need to clear the local storage manually
+
+ it('cy.clearLocalStorage() - clear all data in local storage', function () {
+ // https://on.cypress.io/clearlocalstorage
+ cy.get('.ls-btn').click().should(function () {
+ expect(localStorage.getItem('prop1')).to.eq('red')
+ expect(localStorage.getItem('prop2')).to.eq('blue')
+ expect(localStorage.getItem('prop3')).to.eq('magenta')
+ })
+
+ // clearLocalStorage() yields the localStorage object
+ cy.clearLocalStorage().should(function (ls) {
+ expect(ls.getItem('prop1')).to.be.null
+ expect(ls.getItem('prop2')).to.be.null
+ expect(ls.getItem('prop3')).to.be.null
+ })
+
+ // **** Clear key matching string in Local Storage ****
+ cy.get('.ls-btn').click().should(function () {
+ expect(localStorage.getItem('prop1')).to.eq('red')
+ expect(localStorage.getItem('prop2')).to.eq('blue')
+ expect(localStorage.getItem('prop3')).to.eq('magenta')
+ })
+
+ cy.clearLocalStorage('prop1').should(function (ls) {
+ expect(ls.getItem('prop1')).to.be.null
+ expect(ls.getItem('prop2')).to.eq('blue')
+ expect(ls.getItem('prop3')).to.eq('magenta')
+ })
+
+ // **** Clear key's matching regex in Local Storage ****
+ cy.get('.ls-btn').click().should(function () {
+ expect(localStorage.getItem('prop1')).to.eq('red')
+ expect(localStorage.getItem('prop2')).to.eq('blue')
+ expect(localStorage.getItem('prop3')).to.eq('magenta')
+ })
+
+ cy.clearLocalStorage(/prop1|2/).should(function (ls) {
+ expect(ls.getItem('prop1')).to.be.null
+ expect(ls.getItem('prop2')).to.be.null
+ expect(ls.getItem('prop3')).to.eq('magenta')
+ })
+ })
+ })
+
+ context('Cookies', function () {
+ beforeEach(function () {
+ Cypress.Cookies.debug(true)
+
+ cy.visit('https://example.cypress.io/commands/cookies')
+
+ // clear cookies again after visiting to remove
+ // any 3rd party cookies picked up such as cloudflare
+ cy.clearCookies()
+ })
+
+ it('cy.getCookie() - get a browser cookie', function () {
+ // https://on.cypress.io/getcookie
+ cy.get('#getCookie .set-a-cookie').click()
+
+ // cy.getCookie() yields a cookie object
+ cy.getCookie('token').should('have.property', 'value', '123ABC')
+ })
+
+ it('cy.getCookies() - get browser cookies', function () {
+ // https://on.cypress.io/getcookies
+ cy.getCookies().should('be.empty')
+
+ cy.get('#getCookies .set-a-cookie').click()
+
+ // cy.getCookies() yields an array of cookies
+ cy.getCookies().should('have.length', 1).should(function (cookies) {
+
+ // each cookie has these properties
+ expect(cookies[0]).to.have.property('name', 'token')
+ expect(cookies[0]).to.have.property('value', '123ABC')
+ expect(cookies[0]).to.have.property('httpOnly', false)
+ expect(cookies[0]).to.have.property('secure', false)
+ expect(cookies[0]).to.have.property('domain')
+ expect(cookies[0]).to.have.property('path')
+ })
+ })
+
+ it('cy.setCookie() - set a browser cookie', function () {
+ // https://on.cypress.io/setcookie
+ cy.getCookies().should('be.empty')
+
+ cy.setCookie('foo', 'bar')
+
+ // cy.getCookie() yields a cookie object
+ cy.getCookie('foo').should('have.property', 'value', 'bar')
+ })
+
+ it('cy.clearCookie() - clear a browser cookie', function () {
+ // https://on.cypress.io/clearcookie
+ cy.getCookie('token').should('be.null')
+
+ cy.get('#clearCookie .set-a-cookie').click()
+
+ cy.getCookie('token').should('have.property', 'value', '123ABC')
+
+ // cy.clearCookies() yields null
+ cy.clearCookie('token').should('be.null')
+
+ cy.getCookie('token').should('be.null')
+ })
+
+ it('cy.clearCookies() - clear browser cookies', function () {
+ // https://on.cypress.io/clearcookies
+ cy.getCookies().should('be.empty')
+
+ cy.get('#clearCookies .set-a-cookie').click()
+
+ cy.getCookies().should('have.length', 1)
+
+ // cy.clearCookies() yields null
+ cy.clearCookies()
+
+ cy.getCookies().should('be.empty')
+ })
+ })
+
+ context('Spies, Stubs, and Clock', function () {
+ it('cy.spy() - wrap a method in a spy', function () {
+ // https://on.cypress.io/spy
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+
+ let obj = {
+ foo () {},
+ }
+
+ let spy = cy.spy(obj, 'foo').as('anyArgs')
+
+ obj.foo()
+
+ expect(spy).to.be.called
+
+ })
+
+ it('cy.stub() - create a stub and/or replace a function with a stub', function () {
+ // https://on.cypress.io/stub
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+
+ let obj = {
+ foo () {},
+ }
+
+ let stub = cy.stub(obj, 'foo').as('foo')
+
+ obj.foo('foo', 'bar')
+
+ expect(stub).to.be.called
+
+ })
+
+ it('cy.clock() - control time in the browser', function () {
+ // create the date in UTC so its always the same
+ // no matter what local timezone the browser is running in
+ let now = new Date(Date.UTC(2017, 2, 14)).getTime()
+
+ // https://on.cypress.io/clock
+ cy.clock(now)
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+ cy.get('#clock-div').click()
+ .should('have.text', '1489449600')
+ })
+
+ it('cy.tick() - move time in the browser', function () {
+ // create the date in UTC so its always the same
+ // no matter what local timezone the browser is running in
+ let now = new Date(Date.UTC(2017, 2, 14)).getTime()
+
+ // https://on.cypress.io/tick
+ cy.clock(now)
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+ cy.get('#tick-div').click()
+ .should('have.text', '1489449600')
+ cy.tick(10000) // 10 seconds passed
+ cy.get('#tick-div').click()
+ .should('have.text', '1489449610')
+ })
+ })
+
+ context('Utilities', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/utilities')
+ })
+
+ it('Cypress._.method() - call a lodash method', function () {
+ // use the _.chain, _.map, _.take, and _.value functions
+ // https://on.cypress.io/_
+ cy.request('https://jsonplaceholder.typicode.com/users')
+ .then(function (response) {
+ let ids = Cypress._.chain(response.body).map('id').take(3).value()
+
+ expect(ids).to.deep.eq([1, 2, 3])
+ })
+ })
+
+ it('Cypress.$(selector) - call a jQuery method', function () {
+ // https://on.cypress.io/$
+ let $li = Cypress.$('.utility-jquery li:first')
+
+ cy.wrap($li)
+ .should('not.have.class', 'active')
+ .click()
+ .should('have.class', 'active')
+ })
+
+ it('Cypress.moment() - format or parse dates using a moment method', function () {
+ // use moment's format function
+ // https://on.cypress.io/cypress-moment
+ let time = Cypress.moment().utc('2014-04-25T19:38:53.196Z').format('h:mm A')
+
+ cy.get('.utility-moment').contains('3:38 PM')
+ .should('have.class', 'badge')
+ })
+
+ it('Cypress.Blob.method() - blob utilities and base64 string conversion', function () {
+ cy.get('.utility-blob').then(function ($div) {
+ // https://on.cypress.io/blob
+ // https://github.com/nolanlawson/blob-util#imgSrcToDataURL
+ // get the dataUrl string for the javascript-logo
+ return Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous')
+ .then(function (dataUrl) {
+ // create an element and set its src to the dataUrl
+ let img = Cypress.$(' ', { src: dataUrl })
+ // need to explicitly return cy here since we are initially returning
+ // the Cypress.Blob.imgSrcToDataURL promise to our test
+ // append the image
+ $div.append(img)
+
+ cy.get('.utility-blob img').click()
+ .should('have.attr', 'src', dataUrl)
+ })
+ })
+ })
+
+ it('new Cypress.Promise(function) - instantiate a bluebird promise', function () {
+ // https://on.cypress.io/promise
+ let waited = false
+
+ function waitOneSecond () {
+ // return a promise that resolves after 1 second
+ return new Cypress.Promise(function (resolve, reject) {
+ setTimeout(function () {
+ // set waited to true
+ waited = true
+
+ // resolve with 'foo' string
+ resolve('foo')
+ }, 1000)
+ })
+ }
+
+ cy.then(function () {
+ // return a promise to cy.then() that
+ // is awaited until it resolves
+ return waitOneSecond().then(function (str) {
+ expect(str).to.eq('foo')
+ expect(waited).to.be.true
+ })
+ })
+ })
+ })
+
+
+ context('Cypress.config()', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/cypress-api/config')
+ })
+
+ it('Cypress.config() - get and set configuration options', function () {
+ // https://on.cypress.io/config
+ let myConfig = Cypress.config()
+
+ expect(myConfig).to.have.property('animationDistanceThreshold', 5)
+ expect(myConfig).to.have.property('baseUrl', null)
+ expect(myConfig).to.have.property('defaultCommandTimeout', 4000)
+ expect(myConfig).to.have.property('requestTimeout', 5000)
+ expect(myConfig).to.have.property('responseTimeout', 30000)
+ expect(myConfig).to.have.property('viewportHeight', 660)
+ expect(myConfig).to.have.property('viewportWidth', 1000)
+ expect(myConfig).to.have.property('pageLoadTimeout', 60000)
+ expect(myConfig).to.have.property('waitForAnimations', true)
+
+ expect(Cypress.config('pageLoadTimeout')).to.eq(60000)
+
+ // this will change the config for the rest of your tests!
+ Cypress.config('pageLoadTimeout', 20000)
+
+ expect(Cypress.config('pageLoadTimeout')).to.eq(20000)
+
+ Cypress.config('pageLoadTimeout', 60000)
+ })
+ })
+
+ context('Cypress.env()', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/cypress-api/env')
+ })
+
+ // We can set environment variables for highly dynamic values
+
+ // https://on.cypress.io/environment-variables
+ it('Cypress.env() - get environment variables', function () {
+ // https://on.cypress.io/env
+ // set multiple environment variables
+ Cypress.env({
+ host: 'veronica.dev.local',
+ api_server: 'http://localhost:8888/v1/',
+ })
+
+ // get environment variable
+ expect(Cypress.env('host')).to.eq('veronica.dev.local')
+
+ // set environment variable
+ Cypress.env('api_server', 'http://localhost:8888/v2/')
+ expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/')
+
+ // get all environment variable
+ expect(Cypress.env()).to.have.property('host', 'veronica.dev.local')
+ expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/')
+ })
+ })
+
+ context('Cypress.Cookies', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/cypress-api/cookies')
+ })
+
+ // https://on.cypress.io/cookies
+ it('Cypress.Cookies.debug() - enable or disable debugging', function () {
+ Cypress.Cookies.debug(true)
+
+ // Cypress will now log in the console when
+ // cookies are set or cleared
+ cy.setCookie('fakeCookie', '123ABC')
+ cy.clearCookie('fakeCookie')
+ cy.setCookie('fakeCookie', '123ABC')
+ cy.clearCookie('fakeCookie')
+ cy.setCookie('fakeCookie', '123ABC')
+ })
+
+ it('Cypress.Cookies.preserveOnce() - preserve cookies by key', function () {
+ // normally cookies are reset after each test
+ cy.getCookie('fakeCookie').should('not.be.ok')
+
+ // preserving a cookie will not clear it when
+ // the next test starts
+ cy.setCookie('lastCookie', '789XYZ')
+ Cypress.Cookies.preserveOnce('lastCookie')
+ })
+
+ it('Cypress.Cookies.defaults() - set defaults for all cookies', function () {
+ // now any cookie with the name 'session_id' will
+ // not be cleared before each new test runs
+ Cypress.Cookies.defaults({
+ whitelist: 'session_id',
+ })
+ })
+ })
+
+ context('Cypress.dom', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/cypress-api/dom')
+ })
+
+ // https://on.cypress.io/dom
+ it('Cypress.dom.isHidden() - determine if a DOM element is hidden', function () {
+ let hiddenP = Cypress.$('.dom-p p.hidden').get(0)
+ let visibleP = Cypress.$('.dom-p p.visible').get(0)
+
+ // our first paragraph has css class 'hidden'
+ expect(Cypress.dom.isHidden(hiddenP)).to.be.true
+ expect(Cypress.dom.isHidden(visibleP)).to.be.false
+ })
+ })
+
+ context('Cypress.Server', function () {
+ beforeEach(function () {
+ cy.visit('https://example.cypress.io/cypress-api/server')
+ })
+
+ // Permanently override server options for
+ // all instances of cy.server()
+
+ // https://on.cypress.io/cypress-server
+ it('Cypress.Server.defaults() - change default config of server', function () {
+ Cypress.Server.defaults({
+ delay: 0,
+ force404: false,
+ whitelist (xhr) {
+ // handle custom logic for whitelisting
+ },
+ })
+ })
+ })
+})
diff --git a/cypress/integration/login.spec.js b/cypress/integration/login.spec.js
new file mode 100644
index 00000000..44eabaef
--- /dev/null
+++ b/cypress/integration/login.spec.js
@@ -0,0 +1,54 @@
+///
+const visitBetaBanner = () => {
+ cy.visit('');
+ getDataCy('BetaAgreement').should('be.visible');
+};
+//node_modules/cypress/dist/Cypress/resources/app/packages/launcher
+//chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/popup.html
+const getDataCy = str => cy.get(`[data-cy=${str}]`);
+
+describe('User first visiting', () => {
+ it('should display the beta banner', () => {
+ visitBetaBanner();
+ });
+ it('should redirect the user to mycrypto on beta agreement rejection', () => {
+ visitBetaBanner();
+ cy.get('[data-cy=BetaAgreement-Reject]').click();
+ cy.url().should('eq', 'https://mycrypto.com/');
+ });
+ it('should let the user proceed to the beta site on accept', () => {
+ visitBetaBanner();
+ cy.get('[data-cy=BetaAgreement-Accept]').click();
+ });
+ it('should load the onboarding modal', () => {
+ getDataCy('Modal')
+ .contains('Next')
+ .click()
+ .click()
+ .click()
+ .click()
+ .click()
+ .click()
+ .click()
+ .click()
+ .click();
+ });
+ it('should finish the onboarding modal', () => {
+ getDataCy('Modal')
+ .contains('Done')
+ .click();
+ });
+ it('should present only the onboard modal on reload', () => {
+ window.localStorage.setItem('acknowledged-beta', true);
+ cy.reload();
+ cy.contains('Welcome to MyCrypto.com');
+ });
+
+ it('should present the user the regular load page', () => {
+ window.localStorage.setItem('acknowledged-beta', true);
+ window.localStorage.setItem('onboardStatus', 10);
+ cy.reload();
+
+ cy.contains('Ledger').click();
+ });
+});
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
new file mode 100644
index 00000000..3a2d9733
--- /dev/null
+++ b/cypress/plugins/index.js
@@ -0,0 +1,28 @@
+// ***********************************************************
+// 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)
+
+module.exports = (on, config) => {
+ // `on` is used to hook into various events Cypress emits
+ // `config` is the resolved Cypress config
+
+ on('before:browser:launch', (browser = {}, args) => {
+ console.log(browser, args); // see what all is in here!
+
+ if (browser.name === 'chrome') {
+ args.push('--userDataDir=/home/henry/~/.chrome_dev');
+
+ // whatever you return here becomes the new args
+ return args;
+ }
+ });
+};
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
new file mode 100644
index 00000000..c1f5a772
--- /dev/null
+++ b/cypress/support/commands.js
@@ -0,0 +1,25 @@
+// ***********************************************
+// 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) => { ... })
diff --git a/cypress/support/index.js b/cypress/support/index.js
new file mode 100644
index 00000000..d68db96d
--- /dev/null
+++ b/cypress/support/index.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// 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')
diff --git a/package.json b/package.json
index d698a6a6..25987d5c 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,8 @@
"classnames": "2.2.5",
"electron-updater": "2.21.4",
"ethereum-blockies-base64": "1.0.1",
- "ethereumjs-abi": "git://github.com/ethereumjs/ethereumjs-abi.git#09c3c48fd3bed143df7fa8f36f6f164205e23796",
+ "ethereumjs-abi":
+ "git://github.com/ethereumjs/ethereumjs-abi.git#09c3c48fd3bed143df7fa8f36f6f164205e23796",
"ethereumjs-tx": "1.3.4",
"ethereumjs-util": "5.1.5",
"ethereumjs-wallet": "0.6.0",
@@ -87,6 +88,7 @@
"core-js": "2.5.5",
"coveralls": "3.0.0",
"css-loader": "0.28.11",
+ "cypress": "^2.1.0",
"electron": "1.8.4",
"electron-builder": "20.8.1",
"empty": "0.10.1",
@@ -140,7 +142,7 @@
"worker-loader": "1.1.1"
},
"resolutions": {
- "*/**/@types/react": "16.3.4"
+ "*/**/@types/react": "16.3.11"
},
"scripts": {
"freezer": "webpack --config=./webpack_config/webpack.freezer.js && node ./dist/freezer.js",
@@ -150,14 +152,21 @@
"prebuild": "check-node-version --package",
"build:downloadable": "webpack --mode=production --config webpack_config/webpack.html.js",
"prebuild:downloadable": "check-node-version --package",
- "build:electron": "webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js",
- "build:electron:osx": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js",
- "build:electron:windows": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js",
- "build:electron:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js",
+ "build:electron":
+ "webpack --config webpack_config/webpack.electron-prod.js && node webpack_config/buildElectron.js",
+ "build:electron:osx":
+ "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=osx node webpack_config/buildElectron.js",
+ "build:electron:windows":
+ "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=windows node webpack_config/buildElectron.js",
+ "build:electron:linux":
+ "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=linux node webpack_config/buildElectron.js",
"prebuild:electron": "check-node-version --package",
- "jenkins:build:linux": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js",
- "jenkins:build:mac": "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js",
+ "jenkins:build:linux":
+ "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_LINUX node webpack_config/buildElectron.js",
+ "jenkins:build:mac":
+ "webpack --config webpack_config/webpack.electron-prod.js && ELECTRON_OS=JENKINS_MAC node webpack_config/buildElectron.js",
"jenkins:upload": "node jenkins/upload",
+ "cypress:open": "cypress open",
"test:coverage": "jest --config=jest_config/jest.config.json --coverage",
"test": "jest --config=jest_config/jest.config.json",
"test:unit": "jest --config=jest_config/jest.config.json --coverage",
@@ -169,13 +178,16 @@
"predev": "check-node-version --package",
"dev:https": "HTTPS=true node webpack_config/devServer.js",
"predev:https": "check-node-version --package",
- "dev:electron": "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
+ "dev:electron":
+ "concurrently --kill-others --names 'webpack,electron' 'BUILD_ELECTRON=true node webpack_config/devServer.js' 'webpack --config webpack_config/webpack.electron-dev.js && electron dist/electron-js/main.js'",
"tslint": "tslint --project . --exclude common/vendor/**/*",
"tscheck": "tsc --noEmit",
"start": "npm run dev",
"precommit": "lint-staged",
- "formatAll": "find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
- "prettier:diff": "prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"",
+ "formatAll":
+ "find ./common/ -name '*.ts*' | xargs prettier --write --config ./.prettierrc --config-precedence file-override",
+ "prettier:diff":
+ "prettier --write --config ./.prettierrc --list-different \"common/**/*.ts\" \"common/**/*.tsx\"",
"prepush": "npm run tslint && npm run tscheck",
"update:tokens": "ts-node scripts/update-tokens"
},
diff --git a/yarn.lock b/yarn.lock
index d0ec5982..0f858ff7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -43,6 +43,21 @@
core-js "^2.5.3"
regenerator-runtime "^0.11.1"
+"@cypress/listr-verbose-renderer@0.4.1":
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a"
+ dependencies:
+ chalk "^1.1.3"
+ cli-cursor "^1.0.2"
+ date-fns "^1.27.2"
+ figures "^1.7.0"
+
+"@cypress/xvfb@1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.1.3.tgz#6294a7d1feb751f12302248f2089fc534c4acb7f"
+ dependencies:
+ lodash.once "^4.1.1"
+
"@parity/qr-signer@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@parity/qr-signer/-/qr-signer-0.1.1.tgz#3fd268bba845c37bc06d9aa0abe2a1050d8b73e1"
@@ -60,6 +75,29 @@
dependencies:
"@types/node" "*"
+"@types/blob-util@1.3.3":
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a"
+
+"@types/bluebird@3.5.18":
+ version "3.5.18"
+ resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6"
+
+"@types/chai-jquery@1.1.35":
+ version "1.1.35"
+ resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.35.tgz#9a8f0a39ec0851b2768a8f8c764158c2a2568d04"
+ dependencies:
+ "@types/chai" "*"
+ "@types/jquery" "*"
+
+"@types/chai@*":
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.2.tgz#f1af664769cfb50af805431c407425ed619daa21"
+
+"@types/chai@4.0.8":
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7"
+
"@types/cheerio@*":
version "0.22.7"
resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.7.tgz#4a92eafedfb2b9f4437d3a4410006d81114c66ce"
@@ -96,10 +134,30 @@
version "22.2.3"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.2.3.tgz#0157c0316dc3722c43a7b71de3fdf3acbccef10d"
+"@types/jquery@*":
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.1.tgz#55758d44d422756d6329cbf54e6d41931d7ba28f"
+
+"@types/jquery@3.2.16":
+ version "3.2.16"
+ resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.2.16.tgz#04419c404a3194350e7d3f339a90e72c88db3111"
+
"@types/lodash@4.14.107":
version "4.14.107"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.107.tgz#b2d2ae3958bfb8ff828495cbe12214af9e4d035e"
+"@types/lodash@4.14.87":
+ version "4.14.87"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.87.tgz#55f92183b048c2c64402afe472f8333f4e319a6b"
+
+"@types/minimatch@3.0.1":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.1.tgz#b683eb60be358304ef146f5775db4c0e3696a550"
+
+"@types/mocha@2.2.44":
+ version "2.2.44"
+ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e"
+
"@types/moment-timezone@0.5.4":
version "0.5.4"
resolved "https://registry.yarnpkg.com/@types/moment-timezone/-/moment-timezone-0.5.4.tgz#b600e033f871f3074c283e2bd26fe4f4ec3949d4"
@@ -186,13 +244,7 @@
dependencies:
"@types/react" "*"
-"@types/react@*", "@types/react@16.3.4":
- version "16.3.4"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.4.tgz#9bbb301cd38270ae200bed329a342bd2f140c3ea"
- dependencies:
- csstype "^2.0.0"
-
-"@types/react@16.3.11":
+"@types/react@*", "@types/react@16.3.11":
version "16.3.11"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.3.11.tgz#345a17f1c96420f10b9f0dc696c31091ff9d66ea"
dependencies:
@@ -210,6 +262,21 @@
dependencies:
redux "^3.6.0"
+"@types/sinon-chai@2.7.29":
+ version "2.7.29"
+ resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-2.7.29.tgz#4db01497e2dd1908b2bd30d1782f456353f5f723"
+ dependencies:
+ "@types/chai" "*"
+ "@types/sinon" "*"
+
+"@types/sinon@*":
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.3.1.tgz#32458f9b166cd44c23844eee4937814276f35199"
+
+"@types/sinon@4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.0.0.tgz#9a93ffa4ee1329e85166278a5ed99f81dc4c8362"
+
"@types/url-search-params@^0.10.1":
version "0.10.1"
resolved "https://registry.yarnpkg.com/@types/url-search-params/-/url-search-params-0.10.1.tgz#a5b1f30bf4adad70b103e22c3e1a478869b51be7"
@@ -358,7 +425,7 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
-ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
dependencies:
@@ -621,6 +688,12 @@ async-limiter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
+async@2.1.4:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4"
+ dependencies:
+ lodash "^4.14.0"
+
async@^1.4.0, async@^1.5.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@@ -1476,6 +1549,10 @@ bluebird-lst@^1.0.5:
dependencies:
bluebird "^3.5.1"
+bluebird@3.5.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
+
bluebird@^3.5.0, bluebird@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
@@ -1985,6 +2062,14 @@ chalk@0.5.1:
strip-ansi "^0.3.0"
supports-color "^0.2.0"
+chalk@2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
+ dependencies:
+ ansi-styles "^3.1.0"
+ escape-string-regexp "^1.0.5"
+ supports-color "^4.0.0"
+
chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -2027,6 +2112,10 @@ chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+check-more-types@2.24.0:
+ version "2.24.0"
+ resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
+
check-node-version@3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/check-node-version/-/check-node-version-3.2.0.tgz#783a4292dbf76d6b8294b23abece33682b4a7cce"
@@ -2357,6 +2446,10 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
+commander@2.11.0:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
+
commander@2.15.x, commander@^2.12.1, commander@^2.14.1, commander@^2.5.0, commander@^2.9.0, commander@~2.15.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
@@ -2375,6 +2468,12 @@ commander@~2.8.1:
dependencies:
graceful-readlink ">= 1.0.0"
+common-tags@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0"
+ dependencies:
+ babel-runtime "^6.18.0"
+
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -2848,7 +2947,7 @@ cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
dependencies:
cssom "0.3.x"
-csstype@^2.0.0, csstype@^2.2.0:
+csstype@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.2.0.tgz#1656ef97553ac53b77090844a2531c6660ebd902"
@@ -2870,6 +2969,47 @@ cyclist@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
+cypress@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cypress/-/cypress-2.1.0.tgz#a8bd7d9b89c38a1e380db83b57d9bba0dbb95ba4"
+ dependencies:
+ "@cypress/listr-verbose-renderer" "0.4.1"
+ "@cypress/xvfb" "1.1.3"
+ "@types/blob-util" "1.3.3"
+ "@types/bluebird" "3.5.18"
+ "@types/chai" "4.0.8"
+ "@types/chai-jquery" "1.1.35"
+ "@types/jquery" "3.2.16"
+ "@types/lodash" "4.14.87"
+ "@types/minimatch" "3.0.1"
+ "@types/mocha" "2.2.44"
+ "@types/sinon" "4.0.0"
+ "@types/sinon-chai" "2.7.29"
+ bluebird "3.5.0"
+ chalk "2.1.0"
+ check-more-types "2.24.0"
+ commander "2.11.0"
+ common-tags "1.4.0"
+ debug "3.1.0"
+ extract-zip "1.6.6"
+ fs-extra "4.0.1"
+ getos "2.8.4"
+ glob "7.1.2"
+ is-ci "1.0.10"
+ is-installed-globally "0.1.0"
+ lazy-ass "1.6.0"
+ listr "0.12.0"
+ lodash "4.17.4"
+ minimist "1.2.0"
+ progress "1.1.8"
+ ramda "0.24.1"
+ request "2.81.0"
+ request-progress "0.3.1"
+ supports-color "5.1.0"
+ tmp "0.0.31"
+ url "0.11.0"
+ yauzl "2.8.0"
+
dargs@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829"
@@ -2910,7 +3050,7 @@ debug@2.6.9, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8:
dependencies:
ms "2.0.0"
-debug@^3.0.0, debug@^3.1.0:
+debug@3.1.0, debug@^3.0.0, debug@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@@ -4111,7 +4251,7 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-extract-zip@^1.0.3, extract-zip@^1.6.5:
+extract-zip@1.6.6, extract-zip@^1.0.3, extract-zip@^1.6.5:
version "1.6.6"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c"
dependencies:
@@ -4459,6 +4599,14 @@ fs-extra-p@^4.5.0, fs-extra-p@^4.5.2:
bluebird-lst "^1.0.5"
fs-extra "^5.0.0"
+fs-extra@4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880"
+ dependencies:
+ graceful-fs "^4.1.2"
+ jsonfile "^3.0.0"
+ universalify "^0.1.0"
+
fs-extra@4.0.3, fs-extra@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
@@ -4614,6 +4762,12 @@ get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+getos@2.8.4:
+ version "2.8.4"
+ resolved "https://registry.yarnpkg.com/getos/-/getos-2.8.4.tgz#7b8603d3619c28e38cb0fe7a4f63c3acb80d5163"
+ dependencies:
+ async "2.1.4"
+
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
@@ -4691,6 +4845,17 @@ glob2base@^0.0.12:
dependencies:
find-index "^0.1.1"
+glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
glob@^5.0.15, glob@^5.0.3:
version "5.0.15"
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
@@ -4711,17 +4876,6 @@ glob@^6.0.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1:
- version "7.1.2"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.0.4"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
global-dirs@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
@@ -5013,6 +5167,10 @@ has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
+has-flag@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -5622,6 +5780,12 @@ is-callable@^1.1.1, is-callable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+is-ci@1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e"
+ dependencies:
+ ci-info "^1.0.0"
+
is-ci@^1.0.10, is-ci@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5"
@@ -5760,7 +5924,7 @@ is-hexadecimal@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.1.tgz#6e084bbc92061fbb0971ec58b6ce6d404e24da69"
-is-installed-globally@^0.1.0:
+is-installed-globally@0.1.0, is-installed-globally@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
dependencies:
@@ -6542,6 +6706,12 @@ jsonfile@^2.1.0:
optionalDependencies:
graceful-fs "^4.1.6"
+jsonfile@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -6573,9 +6743,9 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
-"jsqr@git+https://github.com/cozmo/jsQR.git":
+"jsqr@https://github.com/cozmo/jsQR.git":
version "1.0.4"
- resolved "git+https://github.com/cozmo/jsQR.git#d37c764bf43a41ed7c6aa1c17cbdf21b7f9cb69e"
+ resolved "https://github.com/cozmo/jsQR.git#d37c764bf43a41ed7c6aa1c17cbdf21b7f9cb69e"
jssha@2.3.1:
version "2.3.1"
@@ -6655,6 +6825,10 @@ latest-version@^3.0.0:
dependencies:
package-json "^4.0.0"
+lazy-ass@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
+
lazy-cache@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
@@ -6740,6 +6914,19 @@ listr-silent-renderer@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
+listr-update-renderer@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9"
+ dependencies:
+ chalk "^1.1.3"
+ cli-truncate "^0.2.1"
+ elegant-spinner "^1.0.1"
+ figures "^1.7.0"
+ indent-string "^3.0.0"
+ log-symbols "^1.0.2"
+ log-update "^1.0.2"
+ strip-ansi "^3.0.1"
+
listr-update-renderer@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7"
@@ -6762,6 +6949,27 @@ listr-verbose-renderer@^0.4.0:
date-fns "^1.27.2"
figures "^1.7.0"
+listr@0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a"
+ dependencies:
+ chalk "^1.1.3"
+ cli-truncate "^0.2.1"
+ figures "^1.7.0"
+ indent-string "^2.1.0"
+ is-promise "^2.1.0"
+ is-stream "^1.1.0"
+ listr-silent-renderer "^1.1.1"
+ listr-update-renderer "^0.2.0"
+ listr-verbose-renderer "^0.4.0"
+ log-symbols "^1.0.2"
+ log-update "^1.0.2"
+ ora "^0.2.3"
+ p-map "^1.1.1"
+ rxjs "^5.0.0-beta.11"
+ stream-to-observable "^0.1.0"
+ strip-ansi "^3.0.1"
+
listr@^0.13.0:
version "0.13.0"
resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d"
@@ -6942,6 +7150,10 @@ lodash.mergewith@^4.6.0:
version "4.6.1"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
+lodash.once@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+
lodash.restparam@^3.0.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
@@ -6979,6 +7191,10 @@ lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+lodash@4.17.4:
+ version "4.17.4"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
+
lodash@4.17.5, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.1, lodash@~4.17.4:
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
@@ -7983,7 +8199,7 @@ os-locale@^2.0.0:
lcid "^1.0.0"
mem "^1.1.0"
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@@ -8723,7 +8939,7 @@ progress-stream@^1.1.0:
speedometer "~0.1.2"
through2 "~0.2.3"
-progress@^1.1.8:
+progress@1.1.8, progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
@@ -8901,6 +9117,10 @@ railroad-diagrams@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
+ramda@0.24.1:
+ version "0.24.1"
+ resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"
+
randexp@0.4.6:
version "0.4.6"
resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"
@@ -9561,6 +9781,12 @@ replace-ext@1.0.0, replace-ext@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
+request-progress@0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-0.3.1.tgz#0721c105d8a96ac6b2ce8b2c89ae2d5ecfcf6b3a"
+ dependencies:
+ throttleit "~0.0.2"
+
request-progress@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08"
@@ -9816,7 +10042,7 @@ rx@2.3.24:
version "2.3.24"
resolved "https://registry.yarnpkg.com/rx/-/rx-2.3.24.tgz#14f950a4217d7e35daa71bbcbe58eff68ea4b2b7"
-rxjs@^5.4.2, rxjs@^5.5.2:
+rxjs@^5.0.0-beta.11, rxjs@^5.4.2, rxjs@^5.5.2:
version "5.5.10"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045"
dependencies:
@@ -10457,6 +10683,10 @@ stream-to-buffer@^0.1.0:
dependencies:
stream-to "~0.2.0"
+stream-to-observable@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe"
+
stream-to-observable@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10"
@@ -10658,6 +10888,12 @@ sumchecker@^2.0.2:
dependencies:
debug "^2.2.0"
+supports-color@5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5"
+ dependencies:
+ has-flag "^2.0.0"
+
supports-color@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a"
@@ -10672,6 +10908,12 @@ supports-color@^3.1.2, supports-color@^3.2.3:
dependencies:
has-flag "^1.0.0"
+supports-color@^4.0.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
+ dependencies:
+ has-flag "^2.0.0"
+
supports-color@^5.2.0, supports-color@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0"
@@ -10846,7 +11088,7 @@ throat@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
-throttleit@0.0.2:
+throttleit@0.0.2, throttleit@~0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf"
@@ -10908,6 +11150,12 @@ tinycolor2@^1.1.2, tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
+tmp@0.0.31:
+ version "0.0.31"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
+ dependencies:
+ os-tmpdir "~1.0.1"
+
tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
@@ -11423,7 +11671,7 @@ url-to-options@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
-url@^0.11.0:
+url@0.11.0, url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
dependencies:
@@ -12156,6 +12404,13 @@ yauzl@2.4.1:
dependencies:
fd-slicer "~1.0.1"
+yauzl@2.8.0:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.8.0.tgz#79450aff22b2a9c5a41ef54e02db907ccfbf9ee2"
+ dependencies:
+ buffer-crc32 "~0.2.3"
+ fd-slicer "~1.0.1"
+
yauzl@^2.2.1, yauzl@^2.4.2:
version "2.9.1"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f"