element', () => {
+ // https://on.cypress.io/select
+
+ // at first, no option should be selected
+ cy.get('.action-select')
+ .should('have.value', '--Select a fruit--')
+
+ // Select option(s) with matching text content
+ cy.get('.action-select').select('apples')
+ // confirm the apples were selected
+ // note that each value starts with "fr-" in our HTML
+ cy.get('.action-select').should('have.value', 'fr-apples')
+
+ cy.get('.action-select-multiple')
+ .select(['apples', 'oranges', 'bananas'])
+ // when getting multiple values, invoke "val" method first
+ .invoke('val')
+ .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas'])
+
+ // Select option(s) with matching value
+ cy.get('.action-select').select('fr-bananas')
+ // can attach an assertion right away to the element
+ .should('have.value', 'fr-bananas')
+
+ cy.get('.action-select-multiple')
+ .select(['fr-apples', 'fr-oranges', 'fr-bananas'])
+ .invoke('val')
+ .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas'])
+
+ // assert the selected values include oranges
+ cy.get('.action-select-multiple')
+ .invoke('val').should('include', 'fr-oranges')
+ })
+
+ it('.scrollIntoView() - scroll an element into view', () => {
+ // 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('.trigger() - trigger an event on a DOM element', () => {
+ // https://on.cypress.io/trigger
+
+ // To interact with a range input (slider)
+ // we need to set its value & trigger the
+ // event to signal it changed
+
+ // Here, we invoke jQuery's val() method to set
+ // the value and trigger the 'change' event
+ cy.get('.trigger-input-range')
+ .invoke('val', 25)
+ .trigger('change')
+ .get('input[type=range]').siblings('p')
+ .should('have.text', '25')
+ })
+
+ it('cy.scrollTo() - scroll the window or element to a position', () => {
+ // 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 })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/aliasing.cy.js b/cypress/e2e/2-advanced-examples/aliasing.cy.js
new file mode 100644
index 0000000000..a02fb2bb93
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/aliasing.cy.js
@@ -0,0 +1,39 @@
+///
+
+context('Aliasing', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/aliasing')
+ })
+
+ it('.as() - alias a DOM element for later use', () => {
+ // https://on.cypress.io/as
+
+ // Alias a DOM element for use later
+ // We don't have to traverse to the element
+ // later in our code, we reference it with @
+
+ cy.get('.as-table').find('tbody>tr')
+ .first().find('td').first()
+ .find('button').as('firstBtn')
+
+ // when we reference the alias, we place an
+ // @ in front of its name
+ cy.get('@firstBtn').click()
+
+ cy.get('@firstBtn')
+ .should('have.class', 'btn-success')
+ .and('contain', 'Changed')
+ })
+
+ it('.as() - alias a route for later use', () => {
+ // Alias the route to wait for its response
+ cy.intercept('GET', '**/comments/*').as('getComment')
+
+ // we have code that gets a comment when
+ // the button is clicked in scripts.js
+ cy.get('.network-btn').click()
+
+ // https://on.cypress.io/wait
+ cy.wait('@getComment').its('response.statusCode').should('eq', 200)
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/assertions.cy.js b/cypress/e2e/2-advanced-examples/assertions.cy.js
new file mode 100644
index 0000000000..79e3d0e914
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/assertions.cy.js
@@ -0,0 +1,176 @@
+///
+
+context('Assertions', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/assertions')
+ })
+
+ describe('Implicit Assertions', () => {
+ it('.should() - make an assertion about the current subject', () => {
+ // https://on.cypress.io/should
+ cy.get('.assertion-table')
+ .find('tbody tr:last')
+ .should('have.class', 'success')
+ .find('td')
+ .first()
+ // checking the text of the element in various ways
+ .should('have.text', 'Column content')
+ .should('contain', 'Column content')
+ .should('have.html', 'Column content')
+ // chai-jquery uses "is()" to check if element matches selector
+ .should('match', 'td')
+ // to match text content against a regular expression
+ // first need to invoke jQuery method text()
+ // and then match using regular expression
+ .invoke('text')
+ .should('match', /column content/i)
+
+ // a better way to check element's text content against a regular expression
+ // is to use "cy.contains"
+ // https://on.cypress.io/contains
+ cy.get('.assertion-table')
+ .find('tbody tr:last')
+ // finds first element with text content matching regular expression
+ .contains('td', /column content/i)
+ .should('be.visible')
+
+ // for more information about asserting element's text
+ // see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents
+ })
+
+ it('.and() - chain multiple assertions together', () => {
+ // https://on.cypress.io/and
+ cy.get('.assertions-link')
+ .should('have.class', 'active')
+ .and('have.attr', 'href')
+ .and('include', 'cypress.io')
+ })
+ })
+
+ describe('Explicit Assertions', () => {
+ // https://on.cypress.io/assertions
+ it('expect - make an assertion about a specified subject', () => {
+ // We can use Chai's BDD style assertions
+ expect(true).to.be.true
+ const o = { foo: 'bar' }
+
+ expect(o).to.equal(o)
+ expect(o).to.deep.equal({ foo: 'bar' })
+ // matching text using regular expression
+ expect('FooBar').to.match(/bar$/i)
+ })
+
+ it('pass your own callback function to should()', () => {
+ // Pass a function to should that can have any number
+ // of explicit assertions within it.
+ // The ".should(cb)" function will be retried
+ // automatically until it passes all your explicit assertions or times out.
+ cy.get('.assertions-p')
+ .find('p')
+ .should(($p) => {
+ // https://on.cypress.io/$
+ // return an array of texts from all of the p's
+ const texts = $p.map((i, el) => Cypress.$(el).text())
+
+ // jquery map returns jquery object
+ // and .get() convert this to simple array
+ const paragraphs = texts.get()
+
+ // array should have length of 3
+ expect(paragraphs, 'has 3 paragraphs').to.have.length(3)
+
+ // use second argument to expect(...) to provide clear
+ // message with each assertion
+ expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([
+ 'Some text from first p',
+ 'More text from second p',
+ 'And even more text from third p',
+ ])
+ })
+ })
+
+ it('finds element by class name regex', () => {
+ cy.get('.docs-header')
+ .find('div')
+ // .should(cb) callback function will be retried
+ .should(($div) => {
+ expect($div).to.have.length(1)
+
+ const className = $div[0].className
+
+ expect(className).to.match(/heading-/)
+ })
+ // .then(cb) callback is not retried,
+ // it either passes or fails
+ .then(($div) => {
+ expect($div, 'text content').to.have.text('Introduction')
+ })
+ })
+
+ it('can throw any error', () => {
+ cy.get('.docs-header')
+ .find('div')
+ .should(($div) => {
+ if ($div.length !== 1) {
+ // you can throw your own errors
+ throw new Error('Did not find 1 element')
+ }
+
+ const className = $div[0].className
+
+ if (!className.match(/heading-/)) {
+ throw new Error(`Could not find class "heading-" in ${className}`)
+ }
+ })
+ })
+
+ it('matches unknown text between two elements', () => {
+ /**
+ * Text from the first element.
+ * @type {string}
+ */
+ let text
+
+ /**
+ * Normalizes passed text,
+ * useful before comparing text with spaces and different capitalization.
+ * @param {string} s Text to normalize
+ */
+ const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase()
+
+ cy.get('.two-elements')
+ .find('.first')
+ .then(($first) => {
+ // save text from the first element
+ text = normalizeText($first.text())
+ })
+
+ cy.get('.two-elements')
+ .find('.second')
+ .should(($div) => {
+ // we can massage text before comparing
+ const secondText = normalizeText($div.text())
+
+ expect(secondText, 'second text').to.equal(text)
+ })
+ })
+
+ it('assert - assert shape of an object', () => {
+ const person = {
+ name: 'Joe',
+ age: 20,
+ }
+
+ assert.isObject(person, 'value is object')
+ })
+
+ it('retries the should callback until assertions pass', () => {
+ cy.get('#random-number')
+ .should(($div) => {
+ const n = parseFloat($div.text())
+
+ expect(n).to.be.gte(1).and.be.lte(10)
+ })
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/connectors.cy.js b/cypress/e2e/2-advanced-examples/connectors.cy.js
new file mode 100644
index 0000000000..ae8799181d
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/connectors.cy.js
@@ -0,0 +1,97 @@
+///
+
+context('Connectors', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/connectors')
+ })
+
+ it('.each() - iterate over an array of elements', () => {
+ // https://on.cypress.io/each
+ cy.get('.connectors-each-ul>li')
+ .each(($el, index, $list) => {
+ console.log($el, index, $list)
+ })
+ })
+
+ it('.its() - get properties on the current subject', () => {
+ // 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', () => {
+ // 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', () => {
+ // https://on.cypress.io/spread
+ const arr = ['foo', 'bar', 'baz']
+
+ cy.wrap(arr).spread((foo, bar, baz) => {
+ expect(foo).to.eq('foo')
+ expect(bar).to.eq('bar')
+ expect(baz).to.eq('baz')
+ })
+ })
+
+ describe('.then()', () => {
+ it('invokes a callback function with the current subject', () => {
+ // https://on.cypress.io/then
+ cy.get('.connectors-list > li')
+ .then(($lis) => {
+ expect($lis, '3 items').to.have.length(3)
+ expect($lis.eq(0), 'first item').to.contain('Walk the dog')
+ expect($lis.eq(1), 'second item').to.contain('Feed the cat')
+ expect($lis.eq(2), 'third item').to.contain('Write JavaScript')
+ })
+ })
+
+ it('yields the returned value to the next command', () => {
+ cy.wrap(1)
+ .then((num) => {
+ expect(num).to.equal(1)
+
+ return 2
+ })
+ .then((num) => {
+ expect(num).to.equal(2)
+ })
+ })
+
+ it('yields the original subject without return', () => {
+ cy.wrap(1)
+ .then((num) => {
+ expect(num).to.equal(1)
+ // note that nothing is returned from this callback
+ })
+ .then((num) => {
+ // this callback receives the original unchanged value 1
+ expect(num).to.equal(1)
+ })
+ })
+
+ it('yields the value yielded by the last Cypress command inside', () => {
+ cy.wrap(1)
+ .then((num) => {
+ expect(num).to.equal(1)
+ // note how we run a Cypress command
+ // the result yielded by this Cypress command
+ // will be passed to the second ".then"
+ cy.wrap(2)
+ })
+ .then((num) => {
+ // this callback receives the value yielded by "cy.wrap(2)"
+ expect(num).to.equal(2)
+ })
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/cookies.cy.js b/cypress/e2e/2-advanced-examples/cookies.cy.js
new file mode 100644
index 0000000000..31587ff907
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/cookies.cy.js
@@ -0,0 +1,77 @@
+///
+
+context('Cookies', () => {
+ beforeEach(() => {
+ 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', () => {
+ // 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', () => {
+ // 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((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', () => {
+ // 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', () => {
+ // 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', () => {
+ // 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')
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/cypress_api.cy.js b/cypress/e2e/2-advanced-examples/cypress_api.cy.js
new file mode 100644
index 0000000000..777f32cfc9
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/cypress_api.cy.js
@@ -0,0 +1,200 @@
+///
+
+context('Cypress.Commands', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ // https://on.cypress.io/custom-commands
+
+ it('.add() - create a custom command', () => {
+ Cypress.Commands.add('console', {
+ prevSubject: true,
+ }, (subject, method) => {
+ // the previous subject is automatically received
+ // and the commands arguments are shifted
+
+ // allow us to change the console method used
+ method = method || 'log'
+
+ // log the subject to the console
+ console[method]('The subject is', subject)
+
+ // whatever we return becomes the new subject
+ // we don't want to change the subject so
+ // we return whatever was passed in
+ return subject
+ })
+
+ cy.get('button').console('info').then(($button) => {
+ // subject is still $button
+ })
+ })
+})
+
+context('Cypress.Cookies', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ // https://on.cypress.io/cookies
+ it('.debug() - enable or disable debugging', () => {
+ 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('.preserveOnce() - preserve cookies by key', () => {
+ // 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('.defaults() - set defaults for all cookies', () => {
+ // now any cookie with the name 'session_id' will
+ // not be cleared before each new test runs
+ Cypress.Cookies.defaults({
+ preserve: 'session_id',
+ })
+ })
+})
+
+context('Cypress.arch', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ it('Get CPU architecture name of underlying OS', () => {
+ // https://on.cypress.io/arch
+ expect(Cypress.arch).to.exist
+ })
+})
+
+context('Cypress.config()', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ it('Get and set configuration options', () => {
+ // 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.dom', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ // https://on.cypress.io/dom
+ it('.isHidden() - determine if a DOM element is hidden', () => {
+ 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.env()', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ // We can set environment variables for highly dynamic values
+
+ // https://on.cypress.io/environment-variables
+ it('Get environment variables', () => {
+ // 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.log', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ it('Control what is printed to the Command Log', () => {
+ // https://on.cypress.io/cypress-log
+ })
+})
+
+context('Cypress.platform', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ it('Get underlying OS name', () => {
+ // https://on.cypress.io/platform
+ expect(Cypress.platform).to.be.exist
+ })
+})
+
+context('Cypress.version', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ it('Get current version of Cypress being run', () => {
+ // https://on.cypress.io/version
+ expect(Cypress.version).to.be.exist
+ })
+})
+
+context('Cypress.spec', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/cypress-api')
+ })
+
+ it('Get current spec information', () => {
+ // https://on.cypress.io/spec
+ // wrap the object so we can inspect it easily by clicking in the command log
+ cy.wrap(Cypress.spec).should('include.keys', ['name', 'relative', 'absolute'])
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/files.cy.js b/cypress/e2e/2-advanced-examples/files.cy.js
new file mode 100644
index 0000000000..d449c75660
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/files.cy.js
@@ -0,0 +1,87 @@
+///
+
+/// JSON fixture file can be loaded directly using
+// the built-in JavaScript bundler
+const requiredExample = require('../../fixtures/example')
+
+context('Files', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/files')
+ })
+
+ beforeEach(() => {
+ // load example.json fixture file and store
+ // in the test context object
+ cy.fixture('example.json').as('example')
+ })
+
+ it('cy.fixture() - load a fixture', () => {
+ // https://on.cypress.io/fixture
+
+ // Instead of writing a response inline you can
+ // use a fixture file's content.
+
+ // when application makes an Ajax request matching "GET **/comments/*"
+ // Cypress will intercept it and reply with the object in `example.json` fixture
+ cy.intercept('GET', '**/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('response.body')
+ .should('have.property', 'name')
+ .and('include', 'Using fixtures to represent data')
+ })
+
+ it('cy.fixture() or require - load a fixture', function () {
+ // we are inside the "function () { ... }"
+ // callback and can use test context object "this"
+ // "this.example" was loaded in "beforeEach" function callback
+ expect(this.example, 'fixture in the test context')
+ .to.deep.equal(requiredExample)
+
+ // or use "cy.wrap" and "should('deep.equal', ...)" assertion
+ cy.wrap(this.example)
+ .should('deep.equal', requiredExample)
+ })
+
+ it('cy.readFile() - read file contents', () => {
+ // https://on.cypress.io/readfile
+
+ // You can read a file and yield its contents
+ // The filePath is relative to your project's root.
+ cy.readFile(Cypress.config('configFile')).then((config) => {
+ expect(config).to.be.an('string')
+ })
+ })
+
+ it('cy.writeFile() - write to a file', () => {
+ // https://on.cypress.io/writefile
+
+ // You can write to a file
+
+ // Use a response from a request to automatically
+ // generate a fixture file for use later
+ cy.request('https://jsonplaceholder.cypress.io/users')
+ .then((response) => {
+ cy.writeFile('cypress/fixtures/users.json', response.body)
+ })
+
+ cy.fixture('users').should((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((profile) => {
+ expect(profile.name).to.eq('Jane')
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/local_storage.cy.js b/cypress/e2e/2-advanced-examples/local_storage.cy.js
new file mode 100644
index 0000000000..534d8bd9d3
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/local_storage.cy.js
@@ -0,0 +1,52 @@
+///
+
+context('Local Storage', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/local-storage')
+ })
+ // Although local storage is automatically cleared
+ // in between tests to maintain a clean state
+ // sometimes we need to clear the local storage manually
+
+ it('cy.clearLocalStorage() - clear all data in local storage', () => {
+ // https://on.cypress.io/clearlocalstorage
+ cy.get('.ls-btn').click().should(() => {
+ 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((ls) => {
+ expect(ls.getItem('prop1')).to.be.null
+ expect(ls.getItem('prop2')).to.be.null
+ expect(ls.getItem('prop3')).to.be.null
+ })
+
+ cy.get('.ls-btn').click().should(() => {
+ expect(localStorage.getItem('prop1')).to.eq('red')
+ expect(localStorage.getItem('prop2')).to.eq('blue')
+ expect(localStorage.getItem('prop3')).to.eq('magenta')
+ })
+
+ // Clear key matching string in Local Storage
+ cy.clearLocalStorage('prop1').should((ls) => {
+ expect(ls.getItem('prop1')).to.be.null
+ expect(ls.getItem('prop2')).to.eq('blue')
+ expect(ls.getItem('prop3')).to.eq('magenta')
+ })
+
+ cy.get('.ls-btn').click().should(() => {
+ expect(localStorage.getItem('prop1')).to.eq('red')
+ expect(localStorage.getItem('prop2')).to.eq('blue')
+ expect(localStorage.getItem('prop3')).to.eq('magenta')
+ })
+
+ // Clear keys matching regex in Local Storage
+ cy.clearLocalStorage(/prop1|2/).should((ls) => {
+ expect(ls.getItem('prop1')).to.be.null
+ expect(ls.getItem('prop2')).to.be.null
+ expect(ls.getItem('prop3')).to.eq('magenta')
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/location.cy.js b/cypress/e2e/2-advanced-examples/location.cy.js
new file mode 100644
index 0000000000..299867da07
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/location.cy.js
@@ -0,0 +1,32 @@
+///
+
+context('Location', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/location')
+ })
+
+ it('cy.hash() - get the current URL hash', () => {
+ // https://on.cypress.io/hash
+ cy.hash().should('be.empty')
+ })
+
+ it('cy.location() - get window.location', () => {
+ // https://on.cypress.io/location
+ cy.location().should((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', () => {
+ // https://on.cypress.io/url
+ cy.url().should('eq', 'https://example.cypress.io/commands/location')
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/misc.cy.js b/cypress/e2e/2-advanced-examples/misc.cy.js
new file mode 100644
index 0000000000..087d33c0ac
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/misc.cy.js
@@ -0,0 +1,104 @@
+///
+
+context('Misc', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/misc')
+ })
+
+ it('.end() - end the command chain', () => {
+ // https://on.cypress.io/end
+
+ // cy.end is useful when you want to end a chain of commands
+ // and force Cypress to re-query from the root element
+ cy.get('.misc-table').within(() => {
+ // 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', () => {
+ // execute a system command.
+ // so you can take actions necessary for
+ // your test outside the scope of Cypress.
+ // https://on.cypress.io/exec
+
+ // we can use Cypress.platform string to
+ // select appropriate command
+ // https://on.cypress/io/platform
+ cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`)
+
+ // on CircleCI Windows build machines we have a failure to run bash shell
+ // https://github.com/cypress-io/cypress/issues/5169
+ // so skip some of the tests by passing flag "--env circle=true"
+ const isCircleOnWindows = Cypress.platform === 'win32' && Cypress.env('circle')
+
+ if (isCircleOnWindows) {
+ cy.log('Skipping test on CircleCI')
+
+ return
+ }
+
+ // cy.exec problem on Shippable CI
+ // https://github.com/cypress-io/cypress/issues/6718
+ const isShippable = Cypress.platform === 'linux' && Cypress.env('shippable')
+
+ if (isShippable) {
+ cy.log('Skipping test on ShippableCI')
+
+ return
+ }
+
+ cy.exec('echo Jane Lane')
+ .its('stdout').should('contain', 'Jane Lane')
+
+ if (Cypress.platform === 'win32') {
+ cy.exec(`print ${Cypress.config('configFile')}`)
+ .its('stderr').should('be.empty')
+ } else {
+ cy.exec(`cat ${Cypress.config('configFile')}`)
+ .its('stderr').should('be.empty')
+
+ cy.exec('pwd')
+ .its('code').should('eq', 0)
+ }
+ })
+
+ it('cy.focused() - get the DOM element that has focus', () => {
+ // 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')
+ })
+
+ context('Cypress.Screenshot', function () {
+ it('cy.screenshot() - take a screenshot', () => {
+ // https://on.cypress.io/screenshot
+ cy.screenshot('my-image')
+ })
+
+ it('Cypress.Screenshot.defaults() - change default config of screenshots', function () {
+ Cypress.Screenshot.defaults({
+ blackout: ['.foo'],
+ capture: 'viewport',
+ clip: { x: 0, y: 0, width: 200, height: 200 },
+ scale: false,
+ disableTimersAndAnimations: true,
+ screenshotOnRunFailure: true,
+ onBeforeScreenshot () { },
+ onAfterScreenshot () { },
+ })
+ })
+ })
+
+ it('cy.wrap() - wrap an object', () => {
+ // https://on.cypress.io/wrap
+ cy.wrap({ foo: 'bar' })
+ .should('have.property', 'foo')
+ .and('include', 'bar')
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/navigation.cy.js b/cypress/e2e/2-advanced-examples/navigation.cy.js
new file mode 100644
index 0000000000..b85a46890c
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/navigation.cy.js
@@ -0,0 +1,56 @@
+///
+
+context('Navigation', () => {
+ beforeEach(() => {
+ 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', () => {
+ // https://on.cypress.io/go
+
+ cy.location('pathname').should('include', 'navigation')
+
+ cy.go('back')
+ cy.location('pathname').should('not.include', 'navigation')
+
+ cy.go('forward')
+ cy.location('pathname').should('include', 'navigation')
+
+ // clicking back
+ cy.go(-1)
+ cy.location('pathname').should('not.include', 'navigation')
+
+ // clicking forward
+ cy.go(1)
+ cy.location('pathname').should('include', 'navigation')
+ })
+
+ it('cy.reload() - reload the page', () => {
+ // https://on.cypress.io/reload
+ cy.reload()
+
+ // reload the page without using the cache
+ cy.reload(true)
+ })
+
+ it('cy.visit() - visit a remote url', () => {
+ // https://on.cypress.io/visit
+
+ // Visit any sub-domain of your current domain
+
+ // 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
+ expect(typeof contentWindow === 'object').to.be.true
+ },
+ onLoad (contentWindow) {
+ // contentWindow is the remote page's window object
+ expect(typeof contentWindow === 'object').to.be.true
+ },
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/network_requests.cy.js b/cypress/e2e/2-advanced-examples/network_requests.cy.js
new file mode 100644
index 0000000000..11213a0e85
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/network_requests.cy.js
@@ -0,0 +1,163 @@
+///
+
+context('Network Requests', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/network-requests')
+ })
+
+ // Manage HTTP requests in your app
+
+ it('cy.request() - make an XHR request', () => {
+ // https://on.cypress.io/request
+ cy.request('https://jsonplaceholder.cypress.io/comments')
+ .should((response) => {
+ expect(response.status).to.eq(200)
+ // the server sometimes gets an extra comment posted from another machine
+ // which gets returned as 1 extra object
+ expect(response.body).to.have.property('length').and.be.oneOf([500, 501])
+ expect(response).to.have.property('headers')
+ expect(response).to.have.property('duration')
+ })
+ })
+
+ it('cy.request() - verify response using BDD syntax', () => {
+ cy.request('https://jsonplaceholder.cypress.io/comments')
+ .then((response) => {
+ // https://on.cypress.io/assertions
+ expect(response).property('status').to.equal(200)
+ expect(response).property('body').to.have.property('length').and.be.oneOf([500, 501])
+ expect(response).to.include.keys('headers', 'duration')
+ })
+ })
+
+ it('cy.request() with query parameters', () => {
+ // will execute request
+ // https://jsonplaceholder.cypress.io/comments?postId=1&id=3
+ cy.request({
+ url: 'https://jsonplaceholder.cypress.io/comments',
+ qs: {
+ postId: 1,
+ id: 3,
+ },
+ })
+ .its('body')
+ .should('be.an', 'array')
+ .and('have.length', 1)
+ .its('0') // yields first element of the array
+ .should('contain', {
+ postId: 1,
+ id: 3,
+ })
+ })
+
+ it('cy.request() - pass result to the second request', () => {
+ // first, let's find out the userId of the first user we have
+ cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
+ .its('body') // yields the response object
+ .its('0') // yields the first element of the returned list
+ // the above two commands its('body').its('0')
+ // can be written as its('body.0')
+ // if you do not care about TypeScript checks
+ .then((user) => {
+ expect(user).property('id').to.be.a('number')
+ // make a new post on behalf of the user
+ cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
+ userId: user.id,
+ title: 'Cypress Test Runner',
+ body: 'Fast, easy and reliable testing for anything that runs in a browser.',
+ })
+ })
+ // note that the value here is the returned value of the 2nd request
+ // which is the new post object
+ .then((response) => {
+ expect(response).property('status').to.equal(201) // new entity created
+ expect(response).property('body').to.contain({
+ title: 'Cypress Test Runner',
+ })
+
+ // we don't know the exact post id - only that it will be > 100
+ // since JSONPlaceholder has built-in 100 posts
+ expect(response.body).property('id').to.be.a('number')
+ .and.to.be.gt(100)
+
+ // we don't know the user id here - since it was in above closure
+ // so in this test just confirm that the property is there
+ expect(response.body).property('userId').to.be.a('number')
+ })
+ })
+
+ it('cy.request() - save response in the shared test context', () => {
+ // https://on.cypress.io/variables-and-aliases
+ cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
+ .its('body').its('0') // yields the first element of the returned list
+ .as('user') // saves the object in the test context
+ .then(function () {
+ // NOTE đŸ‘€
+ // By the time this callback runs the "as('user')" command
+ // has saved the user object in the test context.
+ // To access the test context we need to use
+ // the "function () { ... }" callback form,
+ // otherwise "this" points at a wrong or undefined object!
+ cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
+ userId: this.user.id,
+ title: 'Cypress Test Runner',
+ body: 'Fast, easy and reliable testing for anything that runs in a browser.',
+ })
+ .its('body').as('post') // save the new post from the response
+ })
+ .then(function () {
+ // When this callback runs, both "cy.request" API commands have finished
+ // and the test context has "user" and "post" objects set.
+ // Let's verify them.
+ expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id)
+ })
+ })
+
+ it('cy.intercept() - route responses to matching requests', () => {
+ // https://on.cypress.io/intercept
+
+ let message = 'whoa, this comment does not exist'
+
+ // Listen to GET to comments/1
+ cy.intercept('GET', '**/comments/*').as('getComment')
+
+ // we have code that gets a comment when
+ // the button is clicked in scripts.js
+ cy.get('.network-btn').click()
+
+ // https://on.cypress.io/wait
+ cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])
+
+ // Listen to POST to comments
+ cy.intercept('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').should(({ request, response }) => {
+ expect(request.body).to.include('email')
+ expect(request.headers).to.have.property('content-type')
+ expect(response && response.body).to.have.property('name', 'Using POST in cy.intercept()')
+ })
+
+ // Stub a response to PUT comments/ ****
+ cy.intercept({
+ method: 'PUT',
+ url: '**/comments/*',
+ }, {
+ statusCode: 404,
+ body: { error: message },
+ headers: { 'access-control-allow-origin': '*' },
+ delayMs: 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)
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/querying.cy.js b/cypress/e2e/2-advanced-examples/querying.cy.js
new file mode 100644
index 0000000000..00970480f6
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/querying.cy.js
@@ -0,0 +1,114 @@
+///
+
+context('Querying', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/querying')
+ })
+
+ // The most commonly used query is 'cy.get()', you can
+ // think of this like the '$' in jQuery
+
+ it('cy.get() - query DOM elements', () => {
+ // https://on.cypress.io/get
+
+ cy.get('#query-btn').should('contain', 'Button')
+
+ cy.get('.query-btn').should('contain', 'Button')
+
+ cy.get('#querying .well>button:first').should('contain', 'Button')
+ // ↲
+ // Use CSS selectors just like jQuery
+
+ cy.get('[data-test-id="test-example"]').should('have.class', 'example')
+
+ // 'cy.get()' yields jQuery object, you can get its attribute
+ // by invoking `.attr()` method
+ cy.get('[data-test-id="test-example"]')
+ .invoke('attr', 'data-test-id')
+ .should('equal', 'test-example')
+
+ // or you can get element's CSS property
+ cy.get('[data-test-id="test-example"]')
+ .invoke('css', 'position')
+ .should('equal', 'static')
+
+ // or use assertions directly during 'cy.get()'
+ // https://on.cypress.io/assertions
+ cy.get('[data-test-id="test-example"]')
+ .should('have.attr', 'data-test-id', 'test-example')
+ .and('have.css', 'position', 'static')
+ })
+
+ it('cy.contains() - query DOM elements with matching content', () => {
+ // 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')
+
+ cy.get('.query-button')
+ .contains('Save Form')
+ .should('have.class', 'btn')
+ })
+
+ it('.within() - query DOM elements within a specific element', () => {
+ // https://on.cypress.io/within
+ cy.get('.query-form').within(() => {
+ 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', () => {
+ // https://on.cypress.io/root
+
+ // By default, root is the document
+ cy.root().should('match', 'html')
+
+ cy.get('.query-ul').within(() => {
+ // In this within, the root is now the ul DOM element
+ cy.root().should('have.class', 'query-ul')
+ })
+ })
+
+ it('best practices - selecting elements', () => {
+ // https://on.cypress.io/best-practices#Selecting-Elements
+ cy.get('[data-cy=best-practices-selecting-elements]').within(() => {
+ // Worst - too generic, no context
+ cy.get('button').click()
+
+ // Bad. Coupled to styling. Highly subject to change.
+ cy.get('.btn.btn-large').click()
+
+ // Average. Coupled to the `name` attribute which has HTML semantics.
+ cy.get('[name=submission]').click()
+
+ // Better. But still coupled to styling or JS event listeners.
+ cy.get('#main').click()
+
+ // Slightly better. Uses an ID but also ensures the element
+ // has an ARIA role attribute
+ cy.get('#main[role=button]').click()
+
+ // Much better. But still coupled to text content that may change.
+ cy.contains('Submit').click()
+
+ // Best. Insulated from all changes.
+ cy.get('[data-cy=submit]').click()
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js b/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js
new file mode 100644
index 0000000000..72d8a213d8
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js
@@ -0,0 +1,203 @@
+///
+// remove no check once Cypress.sinon is typed
+// https://github.com/cypress-io/cypress/issues/6720
+
+context('Spies, Stubs, and Clock', () => {
+ it('cy.spy() - wrap a method in a spy', () => {
+ // https://on.cypress.io/spy
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+
+ const obj = {
+ foo () {},
+ }
+
+ const spy = cy.spy(obj, 'foo').as('anyArgs')
+
+ obj.foo()
+
+ expect(spy).to.be.called
+ })
+
+ it('cy.spy() retries until assertions pass', () => {
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+
+ const obj = {
+ /**
+ * Prints the argument passed
+ * @param x {any}
+ */
+ foo (x) {
+ console.log('obj.foo called with', x)
+ },
+ }
+
+ cy.spy(obj, 'foo').as('foo')
+
+ setTimeout(() => {
+ obj.foo('first')
+ }, 500)
+
+ setTimeout(() => {
+ obj.foo('second')
+ }, 2500)
+
+ cy.get('@foo').should('have.been.calledTwice')
+ })
+
+ it('cy.stub() - create a stub and/or replace a function with stub', () => {
+ // https://on.cypress.io/stub
+ cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
+
+ const obj = {
+ /**
+ * prints both arguments to the console
+ * @param a {string}
+ * @param b {string}
+ */
+ foo (a, b) {
+ console.log('a', a, 'b', b)
+ },
+ }
+
+ const stub = cy.stub(obj, 'foo').as('foo')
+
+ obj.foo('foo', 'bar')
+
+ expect(stub).to.be.called
+ })
+
+ it('cy.clock() - control time in the browser', () => {
+ // https://on.cypress.io/clock
+
+ // create the date in UTC so its always the same
+ // no matter what local timezone the browser is running in
+ const now = new Date(Date.UTC(2017, 2, 14)).getTime()
+
+ 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', () => {
+ // https://on.cypress.io/tick
+
+ // create the date in UTC so its always the same
+ // no matter what local timezone the browser is running in
+ const now = new Date(Date.UTC(2017, 2, 14)).getTime()
+
+ 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')
+ })
+
+ it('cy.stub() matches depending on arguments', () => {
+ // see all possible matchers at
+ // https://sinonjs.org/releases/latest/matchers/
+ const greeter = {
+ /**
+ * Greets a person
+ * @param {string} name
+ */
+ greet (name) {
+ return `Hello, ${name}!`
+ },
+ }
+
+ cy.stub(greeter, 'greet')
+ .callThrough() // if you want non-matched calls to call the real method
+ .withArgs(Cypress.sinon.match.string).returns('Hi')
+ .withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))
+
+ expect(greeter.greet('World')).to.equal('Hi')
+ expect(() => greeter.greet(42)).to.throw('Invalid name')
+ expect(greeter.greet).to.have.been.calledTwice
+
+ // non-matched calls goes the actual method
+ expect(greeter.greet()).to.equal('Hello, undefined!')
+ })
+
+ it('matches call arguments using Sinon matchers', () => {
+ // see all possible matchers at
+ // https://sinonjs.org/releases/latest/matchers/
+ const calculator = {
+ /**
+ * returns the sum of two arguments
+ * @param a {number}
+ * @param b {number}
+ */
+ add (a, b) {
+ return a + b
+ },
+ }
+
+ const spy = cy.spy(calculator, 'add').as('add')
+
+ expect(calculator.add(2, 3)).to.equal(5)
+
+ // if we want to assert the exact values used during the call
+ expect(spy).to.be.calledWith(2, 3)
+
+ // let's confirm "add" method was called with two numbers
+ expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)
+
+ // alternatively, provide the value to match
+ expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))
+
+ // match any value
+ expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)
+
+ // match any value from a list
+ expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)
+
+ /**
+ * Returns true if the given number is even
+ * @param {number} x
+ */
+ const isEven = (x) => x % 2 === 0
+
+ // expect the value to pass a custom predicate function
+ // the second argument to "sinon.match(predicate, message)" is
+ // shown if the predicate does not pass and assertion fails
+ expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)
+
+ /**
+ * Returns a function that checks if a given number is larger than the limit
+ * @param {number} limit
+ * @returns {(x: number) => boolean}
+ */
+ const isGreaterThan = (limit) => (x) => x > limit
+
+ /**
+ * Returns a function that checks if a given number is less than the limit
+ * @param {number} limit
+ * @returns {(x: number) => boolean}
+ */
+ const isLessThan = (limit) => (x) => x < limit
+
+ // you can combine several matchers using "and", "or"
+ expect(spy).to.be.calledWith(
+ Cypress.sinon.match.number,
+ Cypress.sinon.match(isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4')),
+ )
+
+ expect(spy).to.be.calledWith(
+ Cypress.sinon.match.number,
+ Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3)),
+ )
+
+ // matchers can be used from BDD assertions
+ cy.get('@add').should('have.been.calledWith',
+ Cypress.sinon.match.number, Cypress.sinon.match(3))
+
+ // you can alias matchers for shorter test code
+ const { match: M } = Cypress.sinon
+
+ cy.get('@add').should('have.been.calledWith', M.number, M(3))
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/traversal.cy.js b/cypress/e2e/2-advanced-examples/traversal.cy.js
new file mode 100644
index 0000000000..0a3b9d3306
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/traversal.cy.js
@@ -0,0 +1,121 @@
+///
+
+context('Traversal', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/traversal')
+ })
+
+ it('.children() - get child DOM elements', () => {
+ // https://on.cypress.io/children
+ cy.get('.traversal-breadcrumb')
+ .children('.active')
+ .should('contain', 'Data')
+ })
+
+ it('.closest() - get closest ancestor DOM element', () => {
+ // 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', () => {
+ // https://on.cypress.io/eq
+ cy.get('.traversal-list>li')
+ .eq(1).should('contain', 'siamese')
+ })
+
+ it('.filter() - get DOM elements that match the selector', () => {
+ // https://on.cypress.io/filter
+ cy.get('.traversal-nav>li')
+ .filter('.active').should('contain', 'About')
+ })
+
+ it('.find() - get descendant DOM elements of the selector', () => {
+ // https://on.cypress.io/find
+ cy.get('.traversal-pagination')
+ .find('li').find('a')
+ .should('have.length', 7)
+ })
+
+ it('.first() - get first DOM element', () => {
+ // https://on.cypress.io/first
+ cy.get('.traversal-table td')
+ .first().should('contain', '1')
+ })
+
+ it('.last() - get last DOM element', () => {
+ // https://on.cypress.io/last
+ cy.get('.traversal-buttons .btn')
+ .last().should('contain', 'Submit')
+ })
+
+ it('.next() - get next sibling DOM element', () => {
+ // https://on.cypress.io/next
+ cy.get('.traversal-ul')
+ .contains('apples').next().should('contain', 'oranges')
+ })
+
+ it('.nextAll() - get all next sibling DOM elements', () => {
+ // 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', () => {
+ // https://on.cypress.io/nextuntil
+ cy.get('#veggies')
+ .nextUntil('#nuts').should('have.length', 3)
+ })
+
+ it('.not() - remove DOM elements from set of DOM elements', () => {
+ // 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', () => {
+ // https://on.cypress.io/parent
+ cy.get('.traversal-mark')
+ .parent().should('contain', 'Morbi leo risus')
+ })
+
+ it('.parents() - get parent DOM elements from DOM elements', () => {
+ // https://on.cypress.io/parents
+ cy.get('.traversal-cite')
+ .parents().should('match', 'blockquote')
+ })
+
+ it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => {
+ // 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', () => {
+ // https://on.cypress.io/prev
+ cy.get('.birds').find('.active')
+ .prev().should('contain', 'Lorikeets')
+ })
+
+ it('.prevAll() - get all previous sibling DOM elements', () => {
+ // 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', () => {
+ // https://on.cypress.io/prevuntil
+ cy.get('.foods-list').find('#nuts')
+ .prevUntil('#veggies').should('have.length', 3)
+ })
+
+ it('.siblings() - get all sibling DOM elements', () => {
+ // https://on.cypress.io/siblings
+ cy.get('.traversal-pills .active')
+ .siblings().should('have.length', 2)
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/utilities.cy.js b/cypress/e2e/2-advanced-examples/utilities.cy.js
new file mode 100644
index 0000000000..14934c22e8
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/utilities.cy.js
@@ -0,0 +1,108 @@
+///
+
+context('Utilities', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/utilities')
+ })
+
+ it('Cypress._ - call a lodash method', () => {
+ // https://on.cypress.io/_
+ cy.request('https://jsonplaceholder.cypress.io/users')
+ .then((response) => {
+ let ids = Cypress._.chain(response.body).map('id').take(3).value()
+
+ expect(ids).to.deep.eq([1, 2, 3])
+ })
+ })
+
+ it('Cypress.$ - call a jQuery method', () => {
+ // 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.Blob - blob utilities and base64 string conversion', () => {
+ // https://on.cypress.io/blob
+ cy.get('.utility-blob').then(($div) => {
+ // 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((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('Cypress.minimatch - test out glob patterns against strings', () => {
+ // https://on.cypress.io/minimatch
+ let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', {
+ matchBase: true,
+ })
+
+ expect(matching, 'matching wildcard').to.be.true
+
+ matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', {
+ matchBase: true,
+ })
+
+ expect(matching, 'comments').to.be.false
+
+ // ** matches against all downstream path segments
+ matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', {
+ matchBase: true,
+ })
+
+ expect(matching, 'comments').to.be.true
+
+ // whereas * matches only the next path segment
+
+ matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', {
+ matchBase: false,
+ })
+
+ expect(matching, 'comments').to.be.false
+ })
+
+ it('Cypress.Promise - instantiate a bluebird promise', () => {
+ // https://on.cypress.io/promise
+ let waited = false
+
+ /**
+ * @return Bluebird
+ */
+ function waitOneSecond () {
+ // return a promise that resolves after 1 second
+ return new Cypress.Promise((resolve, reject) => {
+ setTimeout(() => {
+ // set waited to true
+ waited = true
+
+ // resolve with 'foo' string
+ resolve('foo')
+ }, 1000)
+ })
+ }
+
+ cy.then(() => {
+ // return a promise to cy.then() that
+ // is awaited until it resolves
+ return waitOneSecond().then((str) => {
+ expect(str).to.eq('foo')
+ expect(waited).to.be.true
+ })
+ })
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/viewport.cy.js b/cypress/e2e/2-advanced-examples/viewport.cy.js
new file mode 100644
index 0000000000..95d3eb457b
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/viewport.cy.js
@@ -0,0 +1,59 @@
+///
+
+context('Viewport', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/viewport')
+ })
+
+ it('cy.viewport() - set the viewport size and dimension', () => {
+ // https://on.cypress.io/viewport
+
+ cy.get('#navbar').should('be.visible')
+ 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 is 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 can be set in cypress.config.{js|ts})
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/waiting.cy.js b/cypress/e2e/2-advanced-examples/waiting.cy.js
new file mode 100644
index 0000000000..c8f0d7c672
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/waiting.cy.js
@@ -0,0 +1,31 @@
+///
+
+context('Waiting', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/waiting')
+ })
+ // BE CAREFUL of adding unnecessary wait times.
+ // https://on.cypress.io/best-practices#Unnecessary-Waiting
+
+ // https://on.cypress.io/wait
+ it('cy.wait() - wait for a specific amount of time', () => {
+ 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)
+ })
+
+ it('cy.wait() - wait for a specific route', () => {
+ // Listen to GET to comments/1
+ cy.intercept('GET', '**/comments/*').as('getComment')
+
+ // we have code that gets a comment when
+ // the button is clicked in scripts.js
+ cy.get('.network-btn').click()
+
+ // wait for GET comments/1
+ cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])
+ })
+})
diff --git a/cypress/e2e/2-advanced-examples/window.cy.js b/cypress/e2e/2-advanced-examples/window.cy.js
new file mode 100644
index 0000000000..f94b64971d
--- /dev/null
+++ b/cypress/e2e/2-advanced-examples/window.cy.js
@@ -0,0 +1,22 @@
+///
+
+context('Window', () => {
+ beforeEach(() => {
+ cy.visit('https://example.cypress.io/commands/window')
+ })
+
+ it('cy.window() - get the global window object', () => {
+ // https://on.cypress.io/window
+ cy.window().should('have.property', 'top')
+ })
+
+ it('cy.document() - get the document object', () => {
+ // https://on.cypress.io/document
+ cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
+ })
+
+ it('cy.title() - get the title', () => {
+ // https://on.cypress.io/title
+ cy.title().should('include', 'Kitchen Sink')
+ })
+})
diff --git a/cypress/e2e/pizza.cy.js b/cypress/e2e/pizza.cy.js
new file mode 100644
index 0000000000..71a8a3ca2b
--- /dev/null
+++ b/cypress/e2e/pizza.cy.js
@@ -0,0 +1,50 @@
+describe("unit test", () => {
+ beforeEach(() => {
+ cy.visit('http://localhost:3000/pizza')
+ })
+
+ //getters
+ const specialInput = () => cy.get('.special-wrapper > input')
+ const originalRadio = () => cy.get('.sauce-wrapper > :nth-child(1) > input')
+ const submitButton = () => cy.get('.submit')
+
+
+
+ it('can type in special instructions', () => {
+ specialInput()
+ .should('have.value', "")
+ .type("no garlic plz")
+ .should('have.value', "no garlic plz")
+ })
+
+ it('can add multiple toppings', () => {
+ cy.get('input[type="checkbox"]')
+ .check()
+ .should('be.checked')
+ })
+
+ it('can select size', () => {
+ cy.get('select')
+ .select('Large')
+ .should('have.value', "large")
+ })
+
+ it('can submit an order', () => {
+ cy.get('select')
+ .select('Large')
+ .should('have.value', "large")
+ cy.get('[type="radio"]')
+ .first()
+ .check()
+ .should('have.value', 'Original Recipe')
+ cy.get('input[type="checkbox"]')
+ .check()
+ .should('be.checked')
+ submitButton()
+ .click()
+ cy.url()
+ .should('eq', 'http://localhost:3000/confirm')
+
+
+ })
+})
\ No newline at end of file
diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json
new file mode 100644
index 0000000000..02e4254378
--- /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"
+}
diff --git a/cypress/support/commands.js b/cypress/support/commands.js
new file mode 100644
index 0000000000..66ea16ef0e
--- /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 will overwrite an existing command --
+// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
\ No newline at end of file
diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js
new file mode 100644
index 0000000000..0e7290a13d
--- /dev/null
+++ b/cypress/support/e2e.js
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/e2e.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')
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index de76e27885..a983c531a4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,9 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
- "react-scripts": "5.0.0"
+ "react-scripts": "5.0.0",
+ "styled-components": "^5.3.6",
+ "yup": "^0.32.11"
}
},
"node_modules/@ampproject/remapping": {
@@ -2043,6 +2045,29 @@
"postcss": "^8.3"
}
},
+ "node_modules/@emotion/is-prop-valid": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz",
+ "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==",
+ "dependencies": {
+ "@emotion/memoize": "^0.8.0"
+ }
+ },
+ "node_modules/@emotion/memoize": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
+ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
+ },
+ "node_modules/@emotion/stylis": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+ },
+ "node_modules/@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+ },
"node_modules/@eslint/eslintrc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz",
@@ -3141,6 +3166,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
+ "node_modules/@types/lodash": {
+ "version": "4.14.189",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz",
+ "integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA=="
+ },
"node_modules/@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -4210,6 +4240,26 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/babel-plugin-styled-components": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+ "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.0",
+ "@babel/helper-module-imports": "^7.16.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "lodash": "^4.17.11",
+ "picomatch": "^2.3.0"
+ },
+ "peerDependencies": {
+ "styled-components": ">= 2"
+ }
+ },
+ "node_modules/babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
+ },
"node_modules/babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
@@ -4522,6 +4572,14 @@
"node": ">= 6"
}
},
+ "node_modules/camelize": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -5051,6 +5109,14 @@
"postcss": "^8.4"
}
},
+ "node_modules/css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/css-declaration-sorter": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz",
@@ -5243,6 +5309,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
+ "node_modules/css-to-react-native": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
+ "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
+ "dependencies": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"node_modules/css-tree": {
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
@@ -9203,6 +9279,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -9562,6 +9643,11 @@
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
},
+ "node_modules/nanoclone": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
+ "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
+ },
"node_modules/nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
@@ -11344,6 +11430,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "node_modules/property-expr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
+ "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA=="
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -12342,6 +12433,11 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -12731,6 +12827,55 @@
"webpack": "^5.0.0"
}
},
+ "node_modules/styled-components": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz",
+ "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/traverse": "^7.4.5",
+ "@emotion/is-prop-valid": "^1.1.0",
+ "@emotion/stylis": "^0.8.4",
+ "@emotion/unitless": "^0.7.4",
+ "babel-plugin-styled-components": ">= 1.12.0",
+ "css-to-react-native": "^3.0.0",
+ "hoist-non-react-statics": "^3.0.0",
+ "shallowequal": "^1.1.0",
+ "supports-color": "^5.5.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/styled-components"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0",
+ "react-dom": ">= 16.8.0",
+ "react-is": ">= 16.8.0"
+ }
+ },
+ "node_modules/styled-components/node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/styled-components/node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/stylehacks": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz",
@@ -13170,6 +13315,11 @@
"node": ">=0.6"
}
},
+ "node_modules/toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
+ },
"node_modules/tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
@@ -14418,6 +14568,23 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/yup": {
+ "version": "0.32.11",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
+ "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
+ "dependencies": {
+ "@babel/runtime": "^7.15.4",
+ "@types/lodash": "^4.14.175",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "nanoclone": "^0.2.1",
+ "property-expr": "^2.0.4",
+ "toposort": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
}
},
"dependencies": {
@@ -15785,6 +15952,29 @@
"postcss-value-parser": "^4.2.0"
}
},
+ "@emotion/is-prop-valid": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz",
+ "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==",
+ "requires": {
+ "@emotion/memoize": "^0.8.0"
+ }
+ },
+ "@emotion/memoize": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz",
+ "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA=="
+ },
+ "@emotion/stylis": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
+ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
+ },
+ "@emotion/unitless": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
+ "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
+ },
"@eslint/eslintrc": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.0.tgz",
@@ -16591,6 +16781,11 @@
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4="
},
+ "@types/lodash": {
+ "version": "4.14.189",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz",
+ "integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA=="
+ },
"@types/mime": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz",
@@ -17396,6 +17591,23 @@
"@babel/helper-define-polyfill-provider": "^0.3.1"
}
},
+ "babel-plugin-styled-components": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
+ "integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.16.0",
+ "@babel/helper-module-imports": "^7.16.0",
+ "babel-plugin-syntax-jsx": "^6.18.0",
+ "lodash": "^4.17.11",
+ "picomatch": "^2.3.0"
+ }
+ },
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
+ },
"babel-plugin-transform-react-remove-prop-types": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz",
@@ -17646,6 +17858,11 @@
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="
},
+ "camelize": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
+ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="
+ },
"caniuse-api": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
@@ -18042,6 +18259,11 @@
"postcss-selector-parser": "^6.0.9"
}
},
+ "css-color-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
+ "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="
+ },
"css-declaration-sorter": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz",
@@ -18101,7 +18323,9 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "requires": {}
+ "requires": {
+ "ajv": "^8.0.0"
+ }
},
"ajv-keywords": {
"version": "5.1.0",
@@ -18152,6 +18376,16 @@
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
},
+ "css-to-react-native": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
+ "integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
+ "requires": {
+ "camelize": "^1.0.0",
+ "css-color-keywords": "^1.0.0",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
"css-tree": {
"version": "1.0.0-alpha.37",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
@@ -21056,6 +21290,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "lodash-es": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+ },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -21248,7 +21487,9 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "requires": {}
+ "requires": {
+ "ajv": "^8.0.0"
+ }
},
"ajv-keywords": {
"version": "5.1.0",
@@ -21321,6 +21562,11 @@
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
},
+ "nanoclone": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz",
+ "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
+ },
"nanoid": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
@@ -22488,6 +22734,11 @@
}
}
},
+ "property-expr": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz",
+ "integrity": "sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA=="
+ },
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -23233,6 +23484,11 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -23525,6 +23781,38 @@
"integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==",
"requires": {}
},
+ "styled-components": {
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz",
+ "integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==",
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/traverse": "^7.4.5",
+ "@emotion/is-prop-valid": "^1.1.0",
+ "@emotion/stylis": "^0.8.4",
+ "@emotion/unitless": "^0.7.4",
+ "babel-plugin-styled-components": ">= 1.12.0",
+ "css-to-react-native": "^3.0.0",
+ "hoist-non-react-statics": "^3.0.0",
+ "shallowequal": "^1.1.0",
+ "supports-color": "^5.5.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
"stylehacks": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz",
@@ -23704,6 +23992,7 @@
"is-glob": "^4.0.3",
"normalize-path": "^3.0.0",
"object-hash": "^2.2.0",
+ "postcss": "^8.4.6",
"postcss-js": "^4.0.0",
"postcss-load-config": "^3.1.0",
"postcss-nested": "5.0.6",
@@ -23848,6 +24137,11 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
},
+ "toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
+ },
"tough-cookie": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz",
@@ -24219,7 +24513,9 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "requires": {}
+ "requires": {
+ "ajv": "^8.0.0"
+ }
},
"ajv-keywords": {
"version": "5.1.0",
@@ -24299,7 +24595,9 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
"integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
- "requires": {}
+ "requires": {
+ "ajv": "^8.0.0"
+ }
},
"ajv-keywords": {
"version": "5.1.0",
@@ -24799,6 +25097,20 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+ },
+ "yup": {
+ "version": "0.32.11",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz",
+ "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==",
+ "requires": {
+ "@babel/runtime": "^7.15.4",
+ "@types/lodash": "^4.14.175",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "nanoclone": "^0.2.1",
+ "property-expr": "^2.0.4",
+ "toposort": "^2.0.2"
+ }
}
}
}
diff --git a/package.json b/package.json
index ff5e577c5d..d3e3d6d58b 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,9 @@
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
- "react-scripts": "5.0.0"
+ "react-scripts": "5.0.0",
+ "styled-components": "^5.3.6",
+ "yup": "^0.32.11"
},
"scripts": {
"start": "react-scripts start",
diff --git a/public/index.html b/public/index.html
index aa069f27cb..9b54e4e9c4 100644
--- a/public/index.html
+++ b/public/index.html
@@ -10,34 +10,11 @@
content="Web site created using create-react-app"
/>
-
-
React App
You need to enable JavaScript to run this app.
-