diff --git a/.eslintignore b/.eslintignore index 21cea94cae50f3d724f9198e9076cc08f4b89082..03f28e4fc83537cd8123a60e44b922e8c612d8ff 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,7 @@ tailwind.config.js prettier.config.js postcss.config.js jest.config.ts +cypress.config.ts commitlint.config.js .gitignore next-env.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index 658e31dfcf75661ecaca6dd9c363d6322e793bd7..c2c5d8bebf09edf9adce7bce3c9693893bd8f0ac 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -21,7 +21,7 @@ "tsx": true } }, - "plugins": ["import", "jsx-a11y", "react", "react-hooks", "@typescript-eslint"], + "plugins": ["import", "jsx-a11y", "react", "react-hooks", "@typescript-eslint", "tailwindcss"], "rules": { "react/function-component-definition": [ 2, @@ -76,7 +76,17 @@ "linebreak-style": ["error", "unix"], "semi": ["error", "always"], "no-cond-assign": ["error", "always"], - "no-trailing-spaces": ["error"] + "no-trailing-spaces": ["error"], + "tailwindcss/classnames-order": "warn", + "tailwindcss/no-custom-classname": "warn", + "tailwindcss/no-contradicting-classname": "error", + "tailwindcss/enforces-shorthand": [ + 1, + { + "callees": ["twMerge"], + "config": "./tailwind.config.ts" + } + ] }, "overrides": [ { @@ -98,6 +108,13 @@ "node": { "extensions": [".js", ".jsx", ".ts", ".tsx"] } + }, + "tailwindcss": { + "callees": ["twMerge"], + "config": "tailwind.config.ts", + "prependCustom": false, + "removeDuplicates": true, + "whitelist": [] } } } diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b427861aa6172d9bf8936696ccca5389247269fe..9c52d89f0723abfac87cc339d56df2b15121ff68 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,6 @@ stages: - lint + - prettier - test - deploy cache: @@ -29,6 +30,19 @@ linter: - merge_requests - tags +prettier: + image: node:16.16.0-alpine + stage: prettier + before_script: + - apk add --no-cache npm + - npm ci + script: + - npm run prettier:ci + only: + - development + - merge_requests + - tags + jest: image: node:16.16.0-alpine stage: test diff --git a/@types/images.d.ts b/@types/images.d.ts deleted file mode 100644 index 902261753088a14cefecf4029b59e7d1c7cfafc6..0000000000000000000000000000000000000000 --- a/@types/images.d.ts +++ /dev/null @@ -1,51 +0,0 @@ -type StaticImageData = { - src: string; - height: number; - width: number; - placeholder?: string; -}; - -declare module '*.png' { - const content: StaticImageData; - export default content; -} - -declare module '*.svg?component' { - const content: React.FC<React.SVGProps<SVGSVGElement>>; - export default content; -} - -declare module '*.svg' { - const content: any; - export default content; -} - -declare module '*.jpg' { - const content: StaticImageData; - export default content; -} - -declare module '*.jpeg' { - const content: StaticImageData; - export default content; -} - -declare module '*.gif' { - const content: StaticImageData; - export default content; -} - -declare module '*.webp' { - const content: StaticImageData; - export default content; -} - -declare module '*.ico' { - const content: StaticImageData; - export default content; -} - -declare module '*.bmp' { - const content: StaticImageData; - export default content; -} diff --git a/cypress/e2e/1-getting-started/todo.cy.js b/cypress/e2e/1-getting-started/todo.cy.js index 4768ff923ece332dbba1054907387d07c58ad6bd..b7dcd36c085f16f6f04f91f6d62004d46282f506 100644 --- a/cypress/e2e/1-getting-started/todo.cy.js +++ b/cypress/e2e/1-getting-started/todo.cy.js @@ -17,26 +17,26 @@ describe('example to-do app', () => { // so we must tell it to visit our website with the `cy.visit()` command. // Since we want to visit the same URL at the start of all our tests, // we include it in our beforeEach function so that it runs before each test - cy.visit('https://example.cypress.io/todo') - }) + cy.visit('https://example.cypress.io/todo'); + }); it('displays two todo items by default', () => { // We use the `cy.get()` command to get all elements that match the selector. // Then, we use `should` to assert that there are two matched items, // which are the two default items. - cy.get('.todo-list li').should('have.length', 2) + cy.get('.todo-list li').should('have.length', 2); // We can go even further and check that the default todos each contain // the correct text. We use the `first` and `last` functions // to get just the first and last matched elements individually, // and then perform an assertion with `should`. - cy.get('.todo-list li').first().should('have.text', 'Pay electric bill') - cy.get('.todo-list li').last().should('have.text', 'Walk the dog') - }) + cy.get('.todo-list li').first().should('have.text', 'Pay electric bill'); + cy.get('.todo-list li').last().should('have.text', 'Walk the dog'); + }); it('can add new todo items', () => { // We'll store our item text in a variable so we can reuse it - const newItem = 'Feed the cat' + const newItem = 'Feed the cat'; // Let's get the input element and use the `type` command to // input our new list item. After typing the content of our item, @@ -44,18 +44,15 @@ describe('example to-do app', () => { // This input has a data-test attribute so we'll use that to select the // element in accordance with best practices: // https://on.cypress.io/selecting-elements - cy.get('[data-test=new-todo]').type(`${newItem}{enter}`) + cy.get('[data-test=new-todo]').type(`${newItem}{enter}`); // Now that we've typed our new item, let's check that it actually was added to the list. // Since it's the newest item, it should exist as the last element in the list. // In addition, with the two default items, we should have a total of 3 elements in the list. // Since assertions yield the element that was asserted on, // we can chain both of these assertions together into a single statement. - cy.get('.todo-list li') - .should('have.length', 3) - .last() - .should('have.text', newItem) - }) + cy.get('.todo-list li').should('have.length', 3).last().should('have.text', newItem); + }); it('can check off an item as completed', () => { // In addition to using the `get` command to get an element by selector, @@ -64,20 +61,15 @@ describe('example to-do app', () => { // In order to check the item, we'll find the <input> element for this <label> // by traversing up the dom to the parent element. From there, we can `find` // the child checkbox <input> element and use the `check` command to check it. - cy.contains('Pay electric bill') - .parent() - .find('input[type=checkbox]') - .check() + cy.contains('Pay electric bill').parent().find('input[type=checkbox]').check(); // Now that we've checked the button, we can go ahead and make sure // that the list element is now marked as completed. // Again we'll use `contains` to find the <label> element and then use the `parents` command // to traverse multiple levels up the dom until we find the corresponding <li> element. // Once we get that element, we can assert that it has the completed class. - cy.contains('Pay electric bill') - .parents('li') - .should('have.class', 'completed') - }) + cy.contains('Pay electric bill').parents('li').should('have.class', 'completed'); + }); context('with a checked task', () => { beforeEach(() => { @@ -85,41 +77,35 @@ describe('example to-do app', () => { // Since we want to perform multiple tests that start with checking // one element, we put it in the beforeEach hook // so that it runs at the start of every test. - cy.contains('Pay electric bill') - .parent() - .find('input[type=checkbox]') - .check() - }) + cy.contains('Pay electric bill').parent().find('input[type=checkbox]').check(); + }); it('can filter for uncompleted tasks', () => { // We'll click on the "active" button in order to // display only incomplete items - cy.contains('Active').click() + cy.contains('Active').click(); // After filtering, we can assert that there is only the one // incomplete item in the list. - cy.get('.todo-list li') - .should('have.length', 1) - .first() - .should('have.text', 'Walk the dog') + cy.get('.todo-list li').should('have.length', 1).first().should('have.text', 'Walk the dog'); // For good measure, let's also assert that the task we checked off // does not exist on the page. - cy.contains('Pay electric bill').should('not.exist') - }) + cy.contains('Pay electric bill').should('not.exist'); + }); it('can filter for completed tasks', () => { // We can perform similar steps as the test above to ensure // that only completed tasks are shown - cy.contains('Completed').click() + cy.contains('Completed').click(); cy.get('.todo-list li') .should('have.length', 1) .first() - .should('have.text', 'Pay electric bill') + .should('have.text', 'Pay electric bill'); - cy.contains('Walk the dog').should('not.exist') - }) + cy.contains('Walk the dog').should('not.exist'); + }); it('can delete all completed tasks', () => { // First, let's click the "Clear completed" button @@ -128,16 +114,14 @@ describe('example to-do app', () => { // This button only appears when at least one task is checked // so this command is implicitly verifying that it does exist. // Second, it selects the button so we can click it. - cy.contains('Clear completed').click() + cy.contains('Clear completed').click(); // Then we can make sure that there is only one element // in the list and our element does not exist - cy.get('.todo-list li') - .should('have.length', 1) - .should('not.have.text', 'Pay electric bill') + cy.get('.todo-list li').should('have.length', 1).should('not.have.text', 'Pay electric bill'); // Finally, make sure that the clear button no longer exists. - cy.contains('Clear completed').should('not.exist') - }) - }) -}) + cy.contains('Clear completed').should('not.exist'); + }); + }); +}); diff --git a/cypress/e2e/2-advanced-examples/actions.cy.js b/cypress/e2e/2-advanced-examples/actions.cy.js index 092637998e28e31357cb95102f50b2e779db6430..758950f8c9478d591ab52559d08a78736dec5317 100644 --- a/cypress/e2e/2-advanced-examples/actions.cy.js +++ b/cypress/e2e/2-advanced-examples/actions.cy.js @@ -2,15 +2,16 @@ context('Actions', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/actions') - }) + cy.visit('https://example.cypress.io/commands/actions'); + }); // https://on.cypress.io/interacting-with-elements it('.type() - type into a DOM element', () => { // https://on.cypress.io/type cy.get('.action-email') - .type('fake@email.com').should('have.value', 'fake@email.com') + .type('fake@email.com') + .should('have.value', 'fake@email.com') // .type() with special character sequences .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') @@ -24,49 +25,53 @@ context('Actions', () => { // Delay each keypress by 0.1 sec .type('slow.typing@email.com', { delay: 100 }) - .should('have.value', 'slow.typing@email.com') + .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') - }) + .should('have.value', 'disabled error checking'); + }); it('.focus() - focus on a DOM element', () => { // https://on.cypress.io/focus - cy.get('.action-focus').focus() + cy.get('.action-focus') + .focus() .should('have.class', 'focus') - .prev().should('have.attr', 'style', 'color: orange;') - }) + .prev() + .should('have.attr', 'style', 'color: orange;'); + }); it('.blur() - blur off a DOM element', () => { // https://on.cypress.io/blur - cy.get('.action-blur').type('About to blur').blur() + cy.get('.action-blur') + .type('About to blur') + .blur() .should('have.class', 'error') - .prev().should('have.attr', 'style', 'color: red;') - }) + .prev() + .should('have.attr', 'style', 'color: red;'); + }); it('.clear() - clears an input or textarea element', () => { // https://on.cypress.io/clear - cy.get('.action-clear').type('Clear this text') + cy.get('.action-clear') + .type('Clear this text') .should('have.value', 'Clear this text') .clear() - .should('have.value', '') - }) + .should('have.value', ''); + }); it('.submit() - submit a form', () => { // https://on.cypress.io/submit - cy.get('.action-form') - .find('[type="text"]').type('HALFOFF') + cy.get('.action-form').find('[type="text"]').type('HALFOFF'); - cy.get('.action-form').submit() - .next().should('contain', 'Your form has been submitted!') - }) + cy.get('.action-form').submit().next().should('contain', 'Your form has been submitted!'); + }); it('.click() - click on a DOM element', () => { // https://on.cypress.io/click - cy.get('.action-btn').click() + cy.get('.action-btn').click(); // You can click on 9 specific positions of an element: // ----------------------------------- @@ -82,16 +87,16 @@ context('Actions', () => { // ----------------------------------- // clicking in the center of the element is the default - cy.get('#action-canvas').click() + 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') + 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 :) @@ -103,117 +108,111 @@ context('Actions', () => { .click(100, 185) .click(125, 190) .click(150, 185) - .click(170, 165) + .click(170, 165); // click multiple elements by passing multiple: true - cy.get('.action-labels>.label').click({ multiple: true }) + cy.get('.action-labels>.label').click({ multiple: true }); // Ignore error checking prior to clicking - cy.get('.action-opacity>.btn').click({ force: true }) - }) + cy.get('.action-opacity>.btn').click({ force: true }); + }); it('.dblclick() - double click on a DOM element', () => { // https://on.cypress.io/dblclick // Our app has a listener on 'dblclick' event in our 'scripts.js' // that hides the div and shows an input on double click - cy.get('.action-div').dblclick().should('not.be.visible') - cy.get('.action-input-hidden').should('be.visible') - }) + cy.get('.action-div').dblclick().should('not.be.visible'); + cy.get('.action-input-hidden').should('be.visible'); + }); it('.rightclick() - right click on a DOM element', () => { // https://on.cypress.io/rightclick // Our app has a listener on 'contextmenu' event in our 'scripts.js' // that hides the div and shows an input on right click - cy.get('.rightclick-action-div').rightclick().should('not.be.visible') - cy.get('.rightclick-action-input-hidden').should('be.visible') - }) + cy.get('.rightclick-action-div').rightclick().should('not.be.visible'); + cy.get('.rightclick-action-input-hidden').should('be.visible'); + }); it('.check() - check a checkbox or radio element', () => { // https://on.cypress.io/check // By default, .check() will check all // matching checkbox or radio elements in succession, one after another - cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') - .check().should('be.checked') + cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]').check().should('be.checked'); - cy.get('.action-radios [type="radio"]').not('[disabled]') - .check().should('be.checked') + cy.get('.action-radios [type="radio"]').not('[disabled]').check().should('be.checked'); // .check() accepts a value argument - cy.get('.action-radios [type="radio"]') - .check('radio1').should('be.checked') + cy.get('.action-radios [type="radio"]').check('radio1').should('be.checked'); // .check() accepts an array of values cy.get('.action-multiple-checkboxes [type="checkbox"]') - .check(['checkbox1', 'checkbox2']).should('be.checked') + .check(['checkbox1', 'checkbox2']) + .should('be.checked'); // Ignore error checking prior to checking - cy.get('.action-checkboxes [disabled]') - .check({ force: true }).should('be.checked') + cy.get('.action-checkboxes [disabled]').check({ force: true }).should('be.checked'); - cy.get('.action-radios [type="radio"]') - .check('radio3', { force: true }).should('be.checked') - }) + cy.get('.action-radios [type="radio"]').check('radio3', { force: true }).should('be.checked'); + }); it('.uncheck() - uncheck a checkbox element', () => { // https://on.cypress.io/uncheck // By default, .uncheck() will uncheck all matching // checkbox elements in succession, one after another - cy.get('.action-check [type="checkbox"]') - .not('[disabled]') - .uncheck().should('not.be.checked') + cy.get('.action-check [type="checkbox"]').not('[disabled]').uncheck().should('not.be.checked'); // .uncheck() accepts a value argument cy.get('.action-check [type="checkbox"]') .check('checkbox1') - .uncheck('checkbox1').should('not.be.checked') + .uncheck('checkbox1') + .should('not.be.checked'); // .uncheck() accepts an array of values cy.get('.action-check [type="checkbox"]') .check(['checkbox1', 'checkbox3']) - .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') + .uncheck(['checkbox1', 'checkbox3']) + .should('not.be.checked'); // Ignore error checking prior to unchecking - cy.get('.action-check [disabled]') - .uncheck({ force: true }).should('not.be.checked') - }) + cy.get('.action-check [disabled]').uncheck({ force: true }).should('not.be.checked'); + }); it('.select() - select an option in a <select> element', () => { // https://on.cypress.io/select // at first, no option should be selected - cy.get('.action-select') - .should('have.value', '--Select a fruit--') + cy.get('.action-select').should('have.value', '--Select a fruit--'); // Select option(s) with matching text content - cy.get('.action-select').select('apples') + 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').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']) + .should('deep.equal', ['fr-apples', 'fr-oranges', 'fr-bananas']); // Select option(s) with matching value - cy.get('.action-select').select('fr-bananas') + cy.get('.action-select') + .select('fr-bananas') // can attach an assertion right away to the element - .should('have.value', 'fr-bananas') + .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']) + .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') - }) + cy.get('.action-select-multiple').invoke('val').should('include', 'fr-oranges'); + }); it('.scrollIntoView() - scroll an element into view', () => { // https://on.cypress.io/scrollintoview @@ -222,27 +221,21 @@ context('Actions', () => { // 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') + 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-horizontal button').scrollIntoView().should('be.visible'); - cy.get('#scroll-vertical button') - .should('not.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-vertical button').scrollIntoView().should('be.visible'); - cy.get('#scroll-both button') - .should('not.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') - }) + cy.get('#scroll-both button').scrollIntoView().should('be.visible'); + }); it('.trigger() - trigger an event on a DOM element', () => { // https://on.cypress.io/trigger @@ -256,9 +249,10 @@ context('Actions', () => { cy.get('.trigger-input-range') .invoke('val', 25) .trigger('change') - .get('input[type=range]').siblings('p') - .should('have.text', '25') - }) + .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 @@ -278,22 +272,22 @@ context('Actions', () => { // if you chain .scrollTo() off of cy, we will // scroll the entire window - cy.scrollTo('bottom') + cy.scrollTo('bottom'); - cy.get('#scrollable-horizontal').scrollTo('right') + 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) + 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%') + cy.get('#scrollable-both').scrollTo('75%', '25%'); // control the easing of the scroll (default is 'swing') - cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }) + cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' }); // control the duration of the scroll (in ms) - cy.get('#scrollable-both').scrollTo('center', { duration: 2000 }) - }) -}) + 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 index a02fb2bb93d7f292d5b72f74a73da64736a7fafe..d9e460dce6daca80cd9051231055493b9565eb47 100644 --- a/cypress/e2e/2-advanced-examples/aliasing.cy.js +++ b/cypress/e2e/2-advanced-examples/aliasing.cy.js @@ -2,8 +2,8 @@ context('Aliasing', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/aliasing') - }) + cy.visit('https://example.cypress.io/commands/aliasing'); + }); it('.as() - alias a DOM element for later use', () => { // https://on.cypress.io/as @@ -12,28 +12,24 @@ context('Aliasing', () => { // 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') + 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').click(); - cy.get('@firstBtn') - .should('have.class', 'btn-success') - .and('contain', 'Changed') - }) + 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') + 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() + cy.get('.network-btn').click(); // https://on.cypress.io/wait - cy.wait('@getComment').its('response.statusCode').should('eq', 200) - }) -}) + 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 index 79e3d0e9148f25c25399af1db435d3f4cce546cc..931cca6bdde87b3fa2393839d4df2f91d2ba78a2 100644 --- a/cypress/e2e/2-advanced-examples/assertions.cy.js +++ b/cypress/e2e/2-advanced-examples/assertions.cy.js @@ -2,8 +2,8 @@ context('Assertions', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/assertions') - }) + cy.visit('https://example.cypress.io/commands/assertions'); + }); describe('Implicit Assertions', () => { it('.should() - make an assertion about the current subject', () => { @@ -23,7 +23,7 @@ context('Assertions', () => { // first need to invoke jQuery method text() // and then match using regular expression .invoke('text') - .should('match', /column content/i) + .should('match', /column content/i); // a better way to check element's text content against a regular expression // is to use "cy.contains" @@ -32,33 +32,33 @@ context('Assertions', () => { .find('tbody tr:last') // finds first <td> element with text content matching regular expression .contains('td', /column content/i) - .should('be.visible') + .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') - }) - }) + .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(true).to.be.true; + const o = { foo: 'bar' }; - expect(o).to.equal(o) - expect(o).to.deep.equal({ 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) - }) + expect('FooBar').to.match(/bar$/i); + }); it('pass your own callback function to should()', () => { // Pass a function to should that can have any number @@ -67,17 +67,17 @@ context('Assertions', () => { // automatically until it passes all your explicit assertions or times out. cy.get('.assertions-p') .find('p') - .should(($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()) + 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() + const paragraphs = texts.get(); // array should have length of 3 - expect(paragraphs, 'has 3 paragraphs').to.have.length(3) + expect(paragraphs, 'has 3 paragraphs').to.have.length(3); // use second argument to expect(...) to provide clear // message with each assertion @@ -85,92 +85,91 @@ context('Assertions', () => { '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) + .should($div => { + expect($div).to.have.length(1); - const className = $div[0].className + const className = $div[0].className; - expect(className).to.match(/heading-/) + 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') - }) - }) + .then($div => { + expect($div, 'text content').to.have.text('Introduction'); + }); + }); it('can throw any error', () => { cy.get('.docs-header') .find('div') - .should(($div) => { + .should($div => { if ($div.length !== 1) { // you can throw your own errors - throw new Error('Did not find 1 element') + throw new Error('Did not find 1 element'); } - const className = $div[0].className + const className = $div[0].className; if (!className.match(/heading-/)) { - throw new Error(`Could not find class "heading-" in ${className}`) + 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 + */ + 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() + */ + const normalizeText = s => s.replace(/\s/g, '').toLowerCase(); cy.get('.two-elements') .find('.first') - .then(($first) => { + .then($first => { // save text from the first element - text = normalizeText($first.text()) - }) + text = normalizeText($first.text()); + }); cy.get('.two-elements') .find('.second') - .should(($div) => { + .should($div => { // we can massage text before comparing - const secondText = normalizeText($div.text()) + const secondText = normalizeText($div.text()); - expect(secondText, 'second text').to.equal(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') - }) + 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) - }) - }) - }) -}) + 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 index f24cf526ed7cfb886b9a3e07b9edd9d48a2fdcd8..101a581c1d33a97c1d0d4e83577d285766f51289 100644 --- a/cypress/e2e/2-advanced-examples/connectors.cy.js +++ b/cypress/e2e/2-advanced-examples/connectors.cy.js @@ -2,97 +2,95 @@ context('Connectors', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/connectors') - }) + 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) - }) - }) + 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) - }) + .should('be.gt', 2); + }); it('.invoke() - invoke a function on the current subject', () => { // our div is hidden in our script.js // $('.connectors-div').hide() - cy.get('.connectors-div').should('be.hidden') + cy.get('.connectors-div').should('be.hidden'); // https://on.cypress.io/invoke // call the jquery method 'show' on the 'div.container' - cy.get('.connectors-div').invoke('show') + cy.get('.connectors-div').invoke('show'); - cy.get('.connectors-div').should('be.visible') - }) + cy.get('.connectors-div').should('be.visible'); + }); it('.spread() - spread an array as individual args to callback function', () => { // https://on.cypress.io/spread - const arr = ['foo', 'bar', 'baz'] + 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') - }) - }) + 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') - }) - }) + 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) + .then(num => { + expect(num).to.equal(1); - return 2 - }) - .then((num) => { - expect(num).to.equal(2) + 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) + .then(num => { + expect(num).to.equal(1); // note that nothing is returned from this callback }) - .then((num) => { + .then(num => { // this callback receives the original unchanged value 1 - expect(num).to.equal(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) + .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) + cy.wrap(2); }) - .then((num) => { + .then(num => { // this callback receives the value yielded by "cy.wrap(2)" - expect(num).to.equal(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 index ddbb9b90c64b5fd0a8081bd3912fe79ec6110000..1caa108043cb4bfcac8489cedf829d23c5869c57 100644 --- a/cypress/e2e/2-advanced-examples/cookies.cy.js +++ b/cypress/e2e/2-advanced-examples/cookies.cy.js @@ -2,117 +2,121 @@ context('Cookies', () => { beforeEach(() => { - Cypress.Cookies.debug(true) + Cypress.Cookies.debug(true); - cy.visit('https://example.cypress.io/commands/cookies') + 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() - }) + cy.clearCookies(); + }); it('cy.getCookie() - get a browser cookie', () => { // https://on.cypress.io/getcookie - cy.get('#getCookie .set-a-cookie').click() + cy.get('#getCookie .set-a-cookie').click(); // cy.getCookie() yields a cookie object - cy.getCookie('token').should('have.property', 'value', '123ABC') - }) + cy.getCookie('token').should('have.property', 'value', '123ABC'); + }); it('cy.getCookies() - get browser cookies for the current domain', () => { // https://on.cypress.io/getcookies - cy.getCookies().should('be.empty') + cy.getCookies().should('be.empty'); - cy.get('#getCookies .set-a-cookie').click() + 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') - }) - }) + 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.getAllCookies() - get all browser cookies', () => { // https://on.cypress.io/getallcookies - cy.getAllCookies().should('be.empty') + cy.getAllCookies().should('be.empty'); - cy.setCookie('key', 'value') - cy.setCookie('key', 'value', { domain: '.example.com' }) + cy.setCookie('key', 'value'); + cy.setCookie('key', 'value', { domain: '.example.com' }); // cy.getAllCookies() yields an array of cookies - cy.getAllCookies().should('have.length', 2).should((cookies) => { - // each cookie has these properties - expect(cookies[0]).to.have.property('name', 'key') - expect(cookies[0]).to.have.property('value', 'value') - 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') - - expect(cookies[1]).to.have.property('name', 'key') - expect(cookies[1]).to.have.property('value', 'value') - expect(cookies[1]).to.have.property('httpOnly', false) - expect(cookies[1]).to.have.property('secure', false) - expect(cookies[1]).to.have.property('domain', '.example.com') - expect(cookies[1]).to.have.property('path') - }) - }) + cy.getAllCookies() + .should('have.length', 2) + .should(cookies => { + // each cookie has these properties + expect(cookies[0]).to.have.property('name', 'key'); + expect(cookies[0]).to.have.property('value', 'value'); + 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'); + + expect(cookies[1]).to.have.property('name', 'key'); + expect(cookies[1]).to.have.property('value', 'value'); + expect(cookies[1]).to.have.property('httpOnly', false); + expect(cookies[1]).to.have.property('secure', false); + expect(cookies[1]).to.have.property('domain', '.example.com'); + expect(cookies[1]).to.have.property('path'); + }); + }); it('cy.setCookie() - set a browser cookie', () => { // https://on.cypress.io/setcookie - cy.getCookies().should('be.empty') + cy.getCookies().should('be.empty'); - cy.setCookie('foo', 'bar') + cy.setCookie('foo', 'bar'); // cy.getCookie() yields a cookie object - cy.getCookie('foo').should('have.property', 'value', 'bar') - }) + 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.getCookie('token').should('be.null'); - cy.get('#clearCookie .set-a-cookie').click() + cy.get('#clearCookie .set-a-cookie').click(); - cy.getCookie('token').should('have.property', 'value', '123ABC') + cy.getCookie('token').should('have.property', 'value', '123ABC'); // cy.clearCookies() yields null - cy.clearCookie('token').should('be.null') + cy.clearCookie('token').should('be.null'); - cy.getCookie('token').should('be.null') - }) + cy.getCookie('token').should('be.null'); + }); it('cy.clearCookies() - clear browser cookies for the current domain', () => { // https://on.cypress.io/clearcookies - cy.getCookies().should('be.empty') + cy.getCookies().should('be.empty'); - cy.get('#clearCookies .set-a-cookie').click() + cy.get('#clearCookies .set-a-cookie').click(); - cy.getCookies().should('have.length', 1) + cy.getCookies().should('have.length', 1); // cy.clearCookies() yields null - cy.clearCookies() + cy.clearCookies(); - cy.getCookies().should('be.empty') - }) + cy.getCookies().should('be.empty'); + }); it('cy.clearAllCookies() - clear all browser cookies', () => { // https://on.cypress.io/clearallcookies - cy.getAllCookies().should('be.empty') + cy.getAllCookies().should('be.empty'); - cy.setCookie('key', 'value') - cy.setCookie('key', 'value', { domain: '.example.com' }) + cy.setCookie('key', 'value'); + cy.setCookie('key', 'value', { domain: '.example.com' }); - cy.getAllCookies().should('have.length', 2) + cy.getAllCookies().should('have.length', 2); // cy.clearAllCookies() yields null - cy.clearAllCookies() + cy.clearAllCookies(); - cy.getAllCookies().should('be.empty') - }) -}) + cy.getAllCookies().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 index 556f2b8ac5fc2c5398f7e7ea3ff7a410be7352a9..7918a69434a29fc1c3fd6a3f5b2aa7d489e3db6e 100644 --- a/cypress/e2e/2-advanced-examples/cypress_api.cy.js +++ b/cypress/e2e/2-advanced-examples/cypress_api.cy.js @@ -1,185 +1,190 @@ /// <reference types="cypress" /> context('Cypress APIs', () => { - context('Cypress.Commands', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + 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 - }) - }) - }) + 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') - }) + cy.visit('https://example.cypress.io/cypress-api'); + }); // https://on.cypress.io/cookies it('.debug() - enable or disable debugging', () => { - Cypress.Cookies.debug(true) + 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') - }) - }) + cy.setCookie('fakeCookie', '123ABC'); + cy.clearCookie('fakeCookie'); + cy.setCookie('fakeCookie', '123ABC'); + cy.clearCookie('fakeCookie'); + cy.setCookie('fakeCookie', '123ABC'); + }); + }); context('Cypress.arch', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + 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 - }) - }) + // https://on.cypress.io/arch + expect(Cypress.arch).to.exist; + }); + }); context('Cypress.config()', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + cy.visit('https://example.cypress.io/cypress-api'); + }); it('Get and set configuration options', () => { - // https://on.cypress.io/config - let myConfig = Cypress.config() + // 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(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) + expect(Cypress.config('pageLoadTimeout')).to.eq(60000); // this will change the config for the rest of your tests! - Cypress.config('pageLoadTimeout', 20000) + Cypress.config('pageLoadTimeout', 20000); - expect(Cypress.config('pageLoadTimeout')).to.eq(20000) + expect(Cypress.config('pageLoadTimeout')).to.eq(20000); - Cypress.config('pageLoadTimeout', 60000) - }) - }) + Cypress.config('pageLoadTimeout', 60000); + }); + }); context('Cypress.dom', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + 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) + 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 - }) - }) + 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') - }) + 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 + // 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') + 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/') + 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/') - }) - }) + 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') - }) + cy.visit('https://example.cypress.io/cypress-api'); + }); it('Control what is printed to the Command Log', () => { - // https://on.cypress.io/cypress-log - }) - }) + // https://on.cypress.io/cypress-log + }); + }); context('Cypress.platform', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + cy.visit('https://example.cypress.io/cypress-api'); + }); it('Get underlying OS name', () => { - // https://on.cypress.io/platform - expect(Cypress.platform).to.be.exist - }) - }) + // https://on.cypress.io/platform + expect(Cypress.platform).to.be.exist; + }); + }); context('Cypress.version', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + 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 - }) - }) + // https://on.cypress.io/version + expect(Cypress.version).to.be.exist; + }); + }); context('Cypress.spec', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/cypress-api') - }) + 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']) - }) - }) -}) + // 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 index 1be9d44b7df7cf6aeb80ca2f572d28e41da13933..fcf6edab98525dbed001d78fa62473dcd677af08 100644 --- a/cypress/e2e/2-advanced-examples/files.cy.js +++ b/cypress/e2e/2-advanced-examples/files.cy.js @@ -2,16 +2,16 @@ /// JSON fixture file can be loaded directly using // the built-in JavaScript bundler -const requiredExample = require('../../fixtures/example') +const requiredExample = require('../../fixtures/example'); context('Files', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/files') + cy.visit('https://example.cypress.io/commands/files'); // load example.json fixture file and store // in the test context object - cy.fixture('example.json').as('example') - }) + cy.fixture('example.json').as('example'); + }); it('cy.fixture() - load a fixture', () => { // https://on.cypress.io/fixture @@ -21,38 +21,37 @@ context('Files', () => { // 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') + 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.get('.fixture-btn').click(); - cy.wait('@getComment').its('response.body') + cy.wait('@getComment') + .its('response.body') .should('have.property', 'name') - .and('include', 'Using fixtures to represent data') - }) + .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) + 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) - }) + 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') - }) - }) + 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 @@ -61,14 +60,13 @@ context('Files', () => { // 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.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 - }) + cy.fixture('users').should(users => { + expect(users[0].name).to.exist; + }); // JavaScript arrays and objects are stringified // and formatted into text. @@ -76,10 +74,10 @@ context('Files', () => { id: 8739, name: 'Jane', email: 'jane@example.com', - }) + }); - cy.fixture('profile').should((profile) => { - expect(profile.name).to.eq('Jane') - }) - }) -}) + cy.fixture('profile').should(profile => { + expect(profile.name).to.eq('Jane'); + }); + }); +}); diff --git a/cypress/e2e/2-advanced-examples/location.cy.js b/cypress/e2e/2-advanced-examples/location.cy.js index 299867da07ef0e82bd055098f01558d9e4230fa4..35dd0bb0beff9031d20b7101f7fd7fb3c3a2be27 100644 --- a/cypress/e2e/2-advanced-examples/location.cy.js +++ b/cypress/e2e/2-advanced-examples/location.cy.js @@ -2,31 +2,31 @@ context('Location', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/location') - }) + 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') - }) + 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 - }) - }) + 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') - }) -}) + 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 index 087d33c0aca706c8d3d1dd418238cd425cbbc9c0..cc4b19a6c069df17c3ad083de29fa78e45e9cb7f 100644 --- a/cypress/e2e/2-advanced-examples/misc.cy.js +++ b/cypress/e2e/2-advanced-examples/misc.cy.js @@ -2,8 +2,8 @@ context('Misc', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/misc') - }) + cy.visit('https://example.cypress.io/commands/misc'); + }); it('.end() - end the command chain', () => { // https://on.cypress.io/end @@ -12,12 +12,12 @@ context('Misc', () => { // 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() + cy.contains('Cheryl').click().end(); // queries the entire table again - cy.contains('Charles').click() - }) - }) + cy.contains('Charles').click(); + }); + }); it('cy.exec() - execute a system command', () => { // execute a system command. @@ -28,58 +28,58 @@ context('Misc', () => { // we can use Cypress.platform string to // select appropriate command // https://on.cypress/io/platform - cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`) + 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') + const isCircleOnWindows = Cypress.platform === 'win32' && Cypress.env('circle'); if (isCircleOnWindows) { - cy.log('Skipping test on CircleCI') + cy.log('Skipping test on CircleCI'); - return + return; } // cy.exec problem on Shippable CI // https://github.com/cypress-io/cypress/issues/6718 - const isShippable = Cypress.platform === 'linux' && Cypress.env('shippable') + const isShippable = Cypress.platform === 'linux' && Cypress.env('shippable'); if (isShippable) { - cy.log('Skipping test on ShippableCI') + cy.log('Skipping test on ShippableCI'); - return + return; } - cy.exec('echo Jane Lane') - .its('stdout').should('contain', 'Jane Lane') + 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') + .its('stderr') + .should('be.empty'); } else { cy.exec(`cat ${Cypress.config('configFile')}`) - .its('stderr').should('be.empty') + .its('stderr') + .should('be.empty'); - cy.exec('pwd') - .its('code').should('eq', 0) + 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('#name').click(); + cy.focused().should('have.id', 'name'); - cy.get('.misc-form').find('#description').click() - cy.focused().should('have.id', 'description') - }) + 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') - }) + cy.screenshot('my-image'); + }); it('Cypress.Screenshot.defaults() - change default config of screenshots', function () { Cypress.Screenshot.defaults({ @@ -89,16 +89,14 @@ context('Misc', () => { scale: false, disableTimersAndAnimations: true, screenshotOnRunFailure: true, - onBeforeScreenshot () { }, - onAfterScreenshot () { }, - }) - }) - }) + onBeforeScreenshot() {}, + onAfterScreenshot() {}, + }); + }); + }); it('cy.wrap() - wrap an object', () => { // https://on.cypress.io/wrap - cy.wrap({ foo: 'bar' }) - .should('have.property', 'foo') - .and('include', 'bar') - }) -}) + 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 index b85a46890c8f946ac3d9b28bb4da4dbe56ea1a89..8a66ac41c3e579b9e335b6a76b3c58f2878ad330 100644 --- a/cypress/e2e/2-advanced-examples/navigation.cy.js +++ b/cypress/e2e/2-advanced-examples/navigation.cy.js @@ -2,38 +2,38 @@ context('Navigation', () => { beforeEach(() => { - cy.visit('https://example.cypress.io') - cy.get('.navbar-nav').contains('Commands').click() - cy.get('.dropdown-menu').contains('Navigation').click() - }) + 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', () => { + it("cy.go() - go back or forward in the browser's history", () => { // https://on.cypress.io/go - cy.location('pathname').should('include', 'navigation') + cy.location('pathname').should('include', 'navigation'); - cy.go('back') - cy.location('pathname').should('not.include', 'navigation') + cy.go('back'); + cy.location('pathname').should('not.include', 'navigation'); - cy.go('forward') - cy.location('pathname').should('include', 'navigation') + cy.go('forward'); + cy.location('pathname').should('include', 'navigation'); // clicking back - cy.go(-1) - cy.location('pathname').should('not.include', 'navigation') + cy.go(-1); + cy.location('pathname').should('not.include', 'navigation'); // clicking forward - cy.go(1) - cy.location('pathname').should('include', 'navigation') - }) + cy.go(1); + cy.location('pathname').should('include', 'navigation'); + }); it('cy.reload() - reload the page', () => { // https://on.cypress.io/reload - cy.reload() + cy.reload(); // reload the page without using the cache - cy.reload(true) - }) + cy.reload(true); + }); it('cy.visit() - visit a remote url', () => { // https://on.cypress.io/visit @@ -43,14 +43,14 @@ context('Navigation', () => { // 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) { + onBeforeLoad(contentWindow) { // contentWindow is the remote page's window object - expect(typeof contentWindow === 'object').to.be.true + expect(typeof contentWindow === 'object').to.be.true; }, - onLoad (contentWindow) { + onLoad(contentWindow) { // contentWindow is the remote page's window object - expect(typeof contentWindow === 'object').to.be.true + 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 index 11213a0e852a4e3515b8b508558f1aea652be904..c8522a7e07a358945772865931133f67d1f59dbf 100644 --- a/cypress/e2e/2-advanced-examples/network_requests.cy.js +++ b/cypress/e2e/2-advanced-examples/network_requests.cy.js @@ -2,33 +2,31 @@ context('Network Requests', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/network-requests') - }) + 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') - }) - }) + 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) => { + 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') - }) - }) + 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 @@ -40,15 +38,15 @@ context('Network Requests', () => { 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, - }) - }) + .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 @@ -58,38 +56,38 @@ context('Network Requests', () => { // 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') + .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 + .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) + 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') - }) - }) + 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 + .its('body') + .its('0') // yields the first element of the returned list .as('user') // saves the object in the test context .then(function () { // NOTE 👀 @@ -103,61 +101,65 @@ context('Network Requests', () => { 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 + .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) - }) - }) + 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' + let message = 'whoa, this comment does not exist'; // Listen to GET to comments/1 - cy.intercept('GET', '**/comments/*').as('getComment') + 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() + cy.get('.network-btn').click(); // https://on.cypress.io/wait - cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304]) + cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304]); // Listen to POST to comments - cy.intercept('POST', '**/comments').as('postComment') + 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.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()') - }) + 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') + 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.get('.network-put').click(); - cy.wait('@putComment') + cy.wait('@putComment'); // our 404 statusCode logic in scripts.js executed - cy.get('.network-put-comment').should('contain', message) - }) -}) + 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 index 00970480f6c8641dd2ee12a2afb417e720417454..f1a7fced84ad47da37b295ec69a5ef799f93711e 100644 --- a/cypress/e2e/2-advanced-examples/querying.cy.js +++ b/cypress/e2e/2-advanced-examples/querying.cy.js @@ -2,8 +2,8 @@ context('Querying', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/querying') - }) + 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 @@ -11,104 +11,92 @@ context('Querying', () => { 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('.query-btn').should('contain', 'Button') + cy.get('.query-btn').should('contain', 'Button'); - cy.get('#querying .well>button:first').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('[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') + .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') + 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') - }) + .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') + 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(/^b\w+/).should('have.class', 'third'); - cy.get('.query-list') - .contains('apples') - .should('have.class', 'first') + 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('#querying').contains('ul', 'oranges').should('have.class', 'query-list'); - cy.get('.query-button') - .contains('Save Form') - .should('have.class', 'btn') - }) + 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') - }) - }) + 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.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') - }) - }) + 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() + cy.get('button').click(); // Bad. Coupled to styling. Highly subject to change. - cy.get('.btn.btn-large').click() + cy.get('.btn.btn-large').click(); // Average. Coupled to the `name` attribute which has HTML semantics. - cy.get('[name=submission]').click() + cy.get('[name=submission]').click(); // Better. But still coupled to styling or JS event listeners. - cy.get('#main').click() + 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() + cy.get('#main[role=button]').click(); // Much better. But still coupled to text content that may change. - cy.contains('Submit').click() + cy.contains('Submit').click(); // Best. Insulated from all changes. - cy.get('[data-cy=submit]').click() - }) - }) -}) + 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 index 88db215d07a60b4b48f2d2d54ecdef53e9735280..0288c9bae1881a26567c1700338dfc887bc11764 100644 --- a/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js +++ b/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js @@ -3,96 +3,93 @@ 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') + cy.visit('https://example.cypress.io/commands/spies-stubs-clocks'); const obj = { - foo () {}, - } + foo() {}, + }; - const spy = cy.spy(obj, 'foo').as('anyArgs') + const spy = cy.spy(obj, 'foo').as('anyArgs'); - obj.foo() + obj.foo(); - expect(spy).to.be.called - }) + expect(spy).to.be.called; + }); it('cy.spy() retries until assertions pass', () => { - cy.visit('https://example.cypress.io/commands/spies-stubs-clocks') + 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) + */ + foo(x) { + console.log('obj.foo called with', x); }, - } + }; - cy.spy(obj, 'foo').as('foo') + cy.spy(obj, 'foo').as('foo'); setTimeout(() => { - obj.foo('first') - }, 500) + obj.foo('first'); + }, 500); setTimeout(() => { - obj.foo('second') - }, 2500) + obj.foo('second'); + }, 2500); - cy.get('@foo').should('have.been.calledTwice') - }) + 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') + 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) + */ + foo(a, b) { + console.log('a', a, 'b', b); }, - } + }; - const stub = cy.stub(obj, 'foo').as('foo') + const stub = cy.stub(obj, 'foo').as('foo'); - obj.foo('foo', 'bar') + obj.foo('foo', 'bar'); - expect(stub).to.be.called - }) + 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() + 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') - }) + 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() + 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.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') - }) + 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 @@ -101,24 +98,26 @@ context('Spies, Stubs, and Clock', () => { /** * Greets a person * @param {string} name - */ - greet (name) { - return `Hello, ${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')) + .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 + 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!') - }) + expect(greeter.greet()).to.equal('Hello, undefined!'); + }); it('matches call arguments using Sinon matchers', () => { // see all possible matchers at @@ -128,74 +127,77 @@ context('Spies, Stubs, and Clock', () => { * returns the sum of two arguments * @param a {number} * @param b {number} - */ - add (a, b) { - return a + b + */ + add(a, b) { + return a + b; }, - } + }; - const spy = cy.spy(calculator, 'add').as('add') + const spy = cy.spy(calculator, 'add').as('add'); - expect(calculator.add(2, 3)).to.equal(5) + 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) + 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) + 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)) + 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) + 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) + 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 + 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) + 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 + 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 + 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)) + 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 + const { match: M } = Cypress.sinon; - cy.get('@add').should('have.been.calledWith', M.number, M(3)) - }) -}) + cy.get('@add').should('have.been.calledWith', M.number, M(3)); + }); +}); diff --git a/cypress/e2e/2-advanced-examples/storage.cy.js b/cypress/e2e/2-advanced-examples/storage.cy.js index c138806ad7bd4abd3fbd5b372d63fbaee1a04c51..d4953b0c29e2c4d218e341922463df0f1dcd37f0 100644 --- a/cypress/e2e/2-advanced-examples/storage.cy.js +++ b/cypress/e2e/2-advanced-examples/storage.cy.js @@ -2,109 +2,115 @@ context('Local Storage / Session Storage', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/storage') - }) + cy.visit('https://example.cypress.io/commands/storage'); + }); // Although localStorage is automatically cleared // in between tests to maintain a clean state // sometimes we need to clear localStorage manually it('cy.clearLocalStorage() - clear all data in localStorage for the current origin', () => { // 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') - }) + 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.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') - }) + 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 localStorage - 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.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') - }) + 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 localStorage - 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') - }) - }) + 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'); + }); + }); it('cy.getAllLocalStorage() - get all data in localStorage for all origins', () => { // https://on.cypress.io/getalllocalstorage - cy.get('.ls-btn').click() + cy.get('.ls-btn').click(); // getAllLocalStorage() yields a map of origins to localStorage values - cy.getAllLocalStorage().should((storageMap) => { + cy.getAllLocalStorage().should(storageMap => { expect(storageMap).to.deep.equal({ // other origins will also be present if localStorage is set on them 'https://example.cypress.io': { - 'prop1': 'red', - 'prop2': 'blue', - 'prop3': 'magenta', + prop1: 'red', + prop2: 'blue', + prop3: 'magenta', }, - }) - }) - }) + }); + }); + }); it('cy.clearAllLocalStorage() - clear all data in localStorage for all origins', () => { // https://on.cypress.io/clearalllocalstorage - cy.get('.ls-btn').click() + cy.get('.ls-btn').click(); // clearAllLocalStorage() yields null cy.clearAllLocalStorage().should(() => { - expect(sessionStorage.getItem('prop1')).to.be.null - expect(sessionStorage.getItem('prop2')).to.be.null - expect(sessionStorage.getItem('prop3')).to.be.null - }) - }) + expect(sessionStorage.getItem('prop1')).to.be.null; + expect(sessionStorage.getItem('prop2')).to.be.null; + expect(sessionStorage.getItem('prop3')).to.be.null; + }); + }); it('cy.getAllSessionStorage() - get all data in sessionStorage for all origins', () => { // https://on.cypress.io/getallsessionstorage - cy.get('.ls-btn').click() + cy.get('.ls-btn').click(); // getAllSessionStorage() yields a map of origins to sessionStorage values - cy.getAllSessionStorage().should((storageMap) => { + cy.getAllSessionStorage().should(storageMap => { expect(storageMap).to.deep.equal({ // other origins will also be present if sessionStorage is set on them 'https://example.cypress.io': { - 'prop4': 'cyan', - 'prop5': 'yellow', - 'prop6': 'black', + prop4: 'cyan', + prop5: 'yellow', + prop6: 'black', }, - }) - }) - }) + }); + }); + }); it('cy.clearAllSessionStorage() - clear all data in sessionStorage for all origins', () => { // https://on.cypress.io/clearallsessionstorage - cy.get('.ls-btn').click() + cy.get('.ls-btn').click(); // clearAllSessionStorage() yields null cy.clearAllSessionStorage().should(() => { - expect(sessionStorage.getItem('prop4')).to.be.null - expect(sessionStorage.getItem('prop5')).to.be.null - expect(sessionStorage.getItem('prop6')).to.be.null - }) - }) -}) + expect(sessionStorage.getItem('prop4')).to.be.null; + expect(sessionStorage.getItem('prop5')).to.be.null; + expect(sessionStorage.getItem('prop6')).to.be.null; + }); + }); +}); diff --git a/cypress/e2e/2-advanced-examples/traversal.cy.js b/cypress/e2e/2-advanced-examples/traversal.cy.js index 0a3b9d33062dacdd49377d462641853ebdf3ba08..5f2c4a4f1d9e7760e188e0bbd5c55867ecb11991 100644 --- a/cypress/e2e/2-advanced-examples/traversal.cy.js +++ b/cypress/e2e/2-advanced-examples/traversal.cy.js @@ -2,120 +2,96 @@ context('Traversal', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/traversal') - }) + 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') - }) + 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') - }) + 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') - }) + 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') - }) + 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) - }) + 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') - }) + 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') - }) + 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') - }) + 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) - }) + 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) - }) + 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') - }) + 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') - }) + 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') - }) + 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) - }) + 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') - }) + 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) - }) + 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) - }) + 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) - }) -}) + 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 index 14934c22e8c1f695f1b669c24ad7598341496c65..34ab6bff71be561b840161939f17cbd1ab8dfcd4 100644 --- a/cypress/e2e/2-advanced-examples/utilities.cy.js +++ b/cypress/e2e/2-advanced-examples/utilities.cy.js @@ -2,107 +2,105 @@ context('Utilities', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/utilities') - }) + 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() + 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]) - }) - }) + 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') + let $li = Cypress.$('.utility-jquery li:first'); - cy.wrap($li) - .should('not.have.class', 'active') - .click() - .should('have.class', 'active') - }) + 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) => { + 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) => { + return Cypress.Blob.imgSrcToDataURL( + 'https://example.cypress.io/assets/img/javascript-logo.png', + undefined, + 'anonymous', + ).then(dataUrl => { // create an <img> element and set its src to the dataUrl - let img = Cypress.$('<img />', { src: dataUrl }) + let img = Cypress.$('<img />', { 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) + $div.append(img); - cy.get('.utility-blob img').click() - .should('have.attr', 'src', dataUrl) - }) - }) - }) + 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 + expect(matching, 'matching wildcard').to.be.true; matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', { matchBase: true, - }) + }); - expect(matching, 'comments').to.be.false + 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 + 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 - }) + expect(matching, 'comments').to.be.false; + }); it('Cypress.Promise - instantiate a bluebird promise', () => { // https://on.cypress.io/promise - let waited = false + let waited = false; /** * @return Bluebird<string> */ - function waitOneSecond () { + function waitOneSecond() { // return a promise that resolves after 1 second return new Cypress.Promise((resolve, reject) => { setTimeout(() => { // set waited to true - waited = true + waited = true; // resolve with 'foo' string - resolve('foo') - }, 1000) - }) + 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 - }) - }) - }) -}) + 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 index 95d3eb457bf4a0ef149f4180984e8b18a22ade01..98fa4955080283aa8c6896de39334557fa6e639b 100644 --- a/cypress/e2e/2-advanced-examples/viewport.cy.js +++ b/cypress/e2e/2-advanced-examples/viewport.cy.js @@ -2,22 +2,22 @@ context('Viewport', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/viewport') - }) + 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) + 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') + 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(2999, 2999); // cy.viewport() accepts a set of preset sizes // to easily set the screen to a device's width and height @@ -25,35 +25,35 @@ context('Viewport', () => { // 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('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) + 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 index c8f0d7c6723d27522415288acc22654c4752f694..a3451a9b60aa9158cc518e87e59565cf13e5a150 100644 --- a/cypress/e2e/2-advanced-examples/waiting.cy.js +++ b/cypress/e2e/2-advanced-examples/waiting.cy.js @@ -2,30 +2,30 @@ context('Waiting', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/waiting') - }) + 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) - }) + 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') + 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() + cy.get('.network-btn').click(); // wait for GET comments/1 - cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304]) - }) -}) + 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 index f94b64971db16f7f69b38958a77897b321ffeec8..2deea94b83a5b9bf661d5e341bfc9059e70bebfc 100644 --- a/cypress/e2e/2-advanced-examples/window.cy.js +++ b/cypress/e2e/2-advanced-examples/window.cy.js @@ -2,21 +2,21 @@ context('Window', () => { beforeEach(() => { - cy.visit('https://example.cypress.io/commands/window') - }) + 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') - }) + 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') - }) + 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') - }) -}) + cy.title().should('include', 'Kitchen Sink'); + }); +}); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 698b01a42c355ca80fdca1c2c979299164d3c7e7..95857aea4cdf2be3812283fa91bdfd73bb9f0a5f 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -34,4 +34,4 @@ // visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element> // } // } -// } \ No newline at end of file +// } diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts index f80f74f8e1f7b4754f5900620d812cde385187d4..598ab5f0d7fe1a8549f89789c4b5b5d483ca9136 100644 --- a/cypress/support/e2e.ts +++ b/cypress/support/e2e.ts @@ -14,7 +14,7 @@ // *********************************************************** // Import commands.js using ES2015 syntax: -import './commands' +import './commands'; // Alternatively you can use CommonJS syntax: -// require('./commands') \ No newline at end of file +// require('./commands') diff --git a/next-env.d.ts b/next-env.d.ts index 53e1f337473e28b0be964f2f31040f09cf4321e8..fd36f9494e2c202c823cdc0f25a6d2e48d352a2b 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,4 +1,5 @@ /// <reference types="next" /> +/// <reference types="next/image-types/global" /> /// <reference types="next/navigation-types/compat/navigation" /> // NOTE: This file should not be edited diff --git a/package-lock.json b/package-lock.json index ac68532e4f8022f66c2bb2e375911d14528a3afe..43cd1bd8311e27d3f964fc7092d58e91efe6c5ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-tailwindcss": "^3.13.0", "eslint-plugin-testing-library": "^6.0.1", "husky": "^8.0.3", "jest": "^29.7.0", @@ -55,6 +56,7 @@ "jest-junit": "^16.0.0", "lint-staged": "^14.0.1", "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.4", "typescript": "^5.2.2" } }, @@ -5863,6 +5865,22 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-tailwindcss": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.13.0.tgz", + "integrity": "sha512-Fcep4KDRLWaK3KmkQbdyKHG0P4GdXFmXdDaweTIPcgOP60OOuWFbh1++dufRT28Q4zpKTKaHwTsXPJ4O/EjU2Q==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.5", + "postcss": "^8.4.4" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "tailwindcss": "^3.3.2" + } + }, "node_modules/eslint-plugin-testing-library": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-6.0.1.tgz", @@ -10507,6 +10525,78 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.4.tgz", + "integrity": "sha512-QZzzB1bID6qPsKHTeA9qPo1APmmxfFrA5DD3LQ+vbTmAnY40eJI7t9Q1ocqel2EKMWNPLJqdTDWZj1hKYgqSgg==", + "dev": true, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/package.json b/package.json index 440bccce90c3ed37f6a3cb82080981c026049918..295ebc421ea7626ebf0e4b2a87bb45a4b7b553fb 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "start": "next start", "lint": "next lint", "lint:ts": "node_modules/eslint/bin/eslint.js src --ext .ts,.tsx", + "prettier:ci": "./node_modules/.bin/prettier --check .", + "format": "next lint && ./node_modules/.bin/prettier --check .", + "format:fix": "next lint --fix && ./node_modules/.bin/prettier --write .", "prepare": "husky install", "postinstall": "husky install", "test": "jest --watch --config ./jest.config.ts", @@ -66,6 +69,7 @@ "eslint-plugin-promise": "^6.1.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-tailwindcss": "^3.13.0", "eslint-plugin-testing-library": "^6.0.1", "husky": "^8.0.3", "jest": "^29.7.0", @@ -73,6 +77,7 @@ "jest-junit": "^16.0.0", "lint-staged": "^14.0.1", "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.4", "typescript": "^5.2.2" }, "config": { diff --git a/prettier.config.js b/prettier.config.js index 6c288d789a4ea26b032f7ad07dc40af4bcc1e7e8..7946df2239da49eb1b21142cd0f61e667ceedefe 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -2,8 +2,10 @@ const config = { singleQuote: true, trailingComma: 'all', printWidth: 100, - 'color-hex-case': 'upper', arrowParens: 'avoid', + plugins: ['prettier-plugin-tailwindcss'], + tailwindConfig: './tailwind.config.ts', + tailwindFunctions: ['twMerge'], }; module.exports = config; diff --git a/src/components/FunctionalArea/FunctionalArea.component.tsx b/src/components/FunctionalArea/FunctionalArea.component.tsx index 22043804efe9662d3a6092d79a2ebf05da414b81..784d7fdeb3f64fa5ee1e14e942fb3a556f558054 100644 --- a/src/components/FunctionalArea/FunctionalArea.component.tsx +++ b/src/components/FunctionalArea/FunctionalArea.component.tsx @@ -4,13 +4,13 @@ import { MapNavigation } from '@/components/FunctionalArea/MapNavigation'; export const FunctionalArea = (): JSX.Element => ( <> - <div className="absolute top-0 left-0 z-10 w-full"> + <div className="absolute left-0 top-0 z-10 w-full"> <TopBar /> </div> <div className="absolute left-[88px] top-16 z-10 w-[calc(100%-88px)]"> <MapNavigation /> </div> - <div className="absolute top-16 left-0 z-10 h-[calc(100%-64px)] flex"> + <div className="absolute left-0 top-16 z-10 flex h-[calc(100%-64px)]"> <NavBar /> </div> </> diff --git a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx index 9aaf502039fe0b5c7f4e8a725c46949708bdb077..75808add8c1e27212e36abb95c3728ddcbe4d78e 100644 --- a/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx +++ b/src/components/FunctionalArea/MapNavigation/MapNavigation.component.tsx @@ -1 +1 @@ -export const MapNavigation = (): JSX.Element => <div className="w-full h-10 bg-slate-200">.</div>; +export const MapNavigation = (): JSX.Element => <div className="h-10 w-full bg-slate-200">.</div>; diff --git a/src/components/FunctionalArea/NavBar/NavBar.component.tsx b/src/components/FunctionalArea/NavBar/NavBar.component.tsx index 3ff6d5f615e99a832ed37bfa7cdeec6b4753100b..91a712ab78031d39bdc89008d1f8bbc7e67ed5b2 100644 --- a/src/components/FunctionalArea/NavBar/NavBar.component.tsx +++ b/src/components/FunctionalArea/NavBar/NavBar.component.tsx @@ -4,9 +4,9 @@ import logoImg from '@/assets/images/logo.png'; import luxembourgLogoImg from '@/assets/images/luxembourg-logo.png'; export const NavBar = (): JSX.Element => ( - <div className="flex items-center justify-between flex-col w-[88px] min-h-100 bg-cultured py-8"> + <div className="flex min-h-full w-[88px] flex-col items-center justify-between bg-cultured py-8"> <div data-testid="nav-buttons"> - <div className="flex flex-col mb-8 gap-[10px]"> + <div className="mb-8 flex flex-col gap-[10px]"> <IconButton icon="info" /> <IconButton icon="page" /> <IconButton icon="plugin" /> @@ -20,14 +20,14 @@ export const NavBar = (): JSX.Element => ( <div className="flex flex-col items-center gap-[20px]" data-testid="nav-logos-and-powered-by"> <Image - className="bg-white-pearl rounded-s rounded-m pb-[7px]" + className="rounded rounded-e rounded-s bg-white-pearl pb-[7px]" src={luxembourgLogoImg} alt="luxembourg logo" height={41} width={48} /> <Image src={logoImg} alt="logo" height={48} width={48} /> - <span className="w-14 h-16 text-[8px] leading-4 text-center"> + <span className="h-16 w-14 text-center text-[8px] leading-4"> Powered by: MINERVA Platform (v16.0.8) </span> </div> diff --git a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx index 785ec99851f53c4fa52a564690e2e590a00cebf4..0c7cdd153ad0db61420f0d1efbe0a6d069ee396e 100644 --- a/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx +++ b/src/components/FunctionalArea/TopBar/SearchBar/SearchBar.component.tsx @@ -17,7 +17,7 @@ export const SearchBar = (): JSX.Element => { aria-label="search-input" data-testid="search-input" onChange={onSearchChange} - className="bg-cultured r-[64px] w-72 rounded-[64px] py-2.5 px-4 text-xs outline-none border border-transparent focus:border-greyscale-600 hover:border-greyscale-600 font-medium text-font-400" + className="h-9 w-72 rounded-[64px] border border-transparent bg-cultured px-4 py-2.5 text-xs font-medium text-font-400 outline-none hover:border-greyscale-600 focus:border-greyscale-600" /> <Image src={lensIcon} diff --git a/src/components/FunctionalArea/TopBar/TopBar.component.tsx b/src/components/FunctionalArea/TopBar/TopBar.component.tsx index a312d04fc328b04af786c62bdb0dc14dc19cea4c..e5a94463f193a32e5a7fd30958e8f7a45649fc7a 100644 --- a/src/components/FunctionalArea/TopBar/TopBar.component.tsx +++ b/src/components/FunctionalArea/TopBar/TopBar.component.tsx @@ -3,7 +3,7 @@ import { UserAvatar } from '@/components/FunctionalArea/TopBar/UserAvatar'; import { Button } from '@/shared/Button'; export const TopBar = (): JSX.Element => ( - <div className="w-100 bg-white h-16 flex flex-row items-center py-4 pl-7 pr-6 justify-between"> + <div className="flex h-16 w-full flex-row items-center justify-between bg-white py-4 pl-7 pr-6"> <div className="flex flex-row items-center"> <UserAvatar /> <SearchBar /> @@ -14,7 +14,7 @@ export const TopBar = (): JSX.Element => ( Overlays </Button> </div> - <div className="bg-primary-100 px-4 py-1 leading-6 text-xs text-primary-500"> + <div className="bg-primary-100 px-4 py-1 text-xs leading-6 text-primary-500"> Parkinson disease map </div> </div> diff --git a/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx index 3dfa2e6cfd26f0b5c7d82aa1049e518b76994e08..cb69b9025e8c74dea2f0ad8feff92c1e8a349d95 100644 --- a/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx +++ b/src/components/FunctionalArea/TopBar/UserAvatar/UserAvatar.component.tsx @@ -2,7 +2,7 @@ import Image from 'next/image'; import avatarImg from '@/assets/images/user-avatar.png'; export const UserAvatar = (): JSX.Element => ( - <div className="w-8 h-8 mr-7" data-testid="user-avatar"> + <div className="mr-7 h-8 w-8" data-testid="user-avatar"> <Image src={avatarImg} alt="user avatar" width={32} height={32} /> </div> ); diff --git a/src/components/Map/Drawer/Drawer.component.tsx b/src/components/Map/Drawer/Drawer.component.tsx index b1b085ec361db7a1295fde9eccf62ca8b3782526..bab067763ad915e6595a45fc09b3ac61e4e55bad 100644 --- a/src/components/Map/Drawer/Drawer.component.tsx +++ b/src/components/Map/Drawer/Drawer.component.tsx @@ -12,7 +12,7 @@ export const Drawer = (): JSX.Element | null => { return ( <> <Button - className="absolute top-[110px] left-[100px] z-10 peer" + className="peer absolute left-[100px] top-[110px] z-10" onClick={(): void => setOpenDrawer(true)} > Open Drawer @@ -20,12 +20,12 @@ export const Drawer = (): JSX.Element | null => { <div className={twMerge( - 'absolute top-[104px] left-[88px] z-10 w-[432px] h-calc-drawer bg-white-pearl text-font-500 transition-all duration-500 transform -translate-x-full', + 'absolute left-[88px] top-[104px] z-10 h-calc-drawer w-[432px] -translate-x-full transform bg-white-pearl text-font-500 transition-all duration-500', open && 'translate-x-0', )} role={drawerRole} > - <div className="flex justify-between items-center text-xl px-6 py-8 border-b border-b-divide"> + <div className="flex items-center justify-between border-b border-b-divide px-6 py-8 text-xl"> <div> <span className="font-normal">Search: </span> <span className="font-semibold">NADH</span> diff --git a/src/components/Map/Map.component.tsx b/src/components/Map/Map.component.tsx index b2b8b6cbc78f7eac4d945b30b8d0de97d57cee6b..3ed96149408bae0f5642295efb4d1d061dc3c533 100644 --- a/src/components/Map/Map.component.tsx +++ b/src/components/Map/Map.component.tsx @@ -3,7 +3,7 @@ import { Drawer } from '@/components/Map/Drawer'; import Image from 'next/image'; export const Map = (): JSX.Element => ( - <div className="w-100 h-screen bg-black relative z-0" data-testid="map-container"> + <div className="relative z-0 h-screen w-full bg-black" data-testid="map-container"> <Drawer /> <Image src={mapImg} fill sizes="100vw" alt="map" className="z-0" /> </div> diff --git a/src/shared/Button/Button.component.tsx b/src/shared/Button/Button.component.tsx index a81da4df775049a5393793b2ae58d810a5695f39..a7f831e7ed22cdc6c8ad17dafd1563375b27144e 100644 --- a/src/shared/Button/Button.component.tsx +++ b/src/shared/Button/Button.component.tsx @@ -57,7 +57,7 @@ export const Button = ({ <button className={twMerge( paddings(), - 'flex items-center group py-2 rounded-s rounded-e', + 'group flex items-center rounded-e rounded-s py-2', variants[variantStyles].button, className, isFrontIcon && 'flex-row-reverse', diff --git a/src/shared/IconButton/IconButton.component.tsx b/src/shared/IconButton/IconButton.component.tsx index 847832941d30d77ccc155fd5c4086562a2ce4ad5..be43b0d0d27d9e89016dffe112b2eb44b768f223 100644 --- a/src/shared/IconButton/IconButton.component.tsx +++ b/src/shared/IconButton/IconButton.component.tsx @@ -30,7 +30,7 @@ export const IconButton = ({ return ( <button className={twMerge( - 'w-10 h-10 flex items-center justify-center group py-2 rounded-s rounded-e', + 'group flex h-10 w-10 items-center justify-center rounded-e rounded-s py-2', 'bg-cultured active:bg-white-pearl', className, isActive && 'bg-white-pearl', diff --git a/src/styles/contrib/normalize.css b/src/styles/contrib/normalize.css index 192eb9ce43389039996bc2e9344c5bb14b730d72..2768db43cefb76c9262ad56ca2a85b683228c5c0 100644 --- a/src/styles/contrib/normalize.css +++ b/src/styles/contrib/normalize.css @@ -174,7 +174,8 @@ textarea { */ button, -input { /* 1 */ +input { + /* 1 */ overflow: visible; } @@ -184,7 +185,8 @@ input { /* 1 */ */ button, -select { /* 1 */ +select { + /* 1 */ text-transform: none; } @@ -193,9 +195,9 @@ select { /* 1 */ */ button, -[type="button"], -[type="reset"], -[type="submit"] { +[type='button'], +[type='reset'], +[type='submit'] { -webkit-appearance: button; } @@ -204,9 +206,9 @@ button, */ button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { +[type='button']::-moz-focus-inner, +[type='reset']::-moz-focus-inner, +[type='submit']::-moz-focus-inner { border-style: none; padding: 0; } @@ -216,9 +218,9 @@ button::-moz-focus-inner, */ button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { +[type='button']:-moz-focusring, +[type='reset']:-moz-focusring, +[type='submit']:-moz-focusring { outline: 1px dotted ButtonText; } @@ -267,8 +269,8 @@ textarea { * 2. Remove the padding in IE 10. */ -[type="checkbox"], -[type="radio"] { +[type='checkbox'], +[type='radio'] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } @@ -277,8 +279,8 @@ textarea { * Correct the cursor style of increment and decrement buttons in Chrome. */ -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { +[type='number']::-webkit-inner-spin-button, +[type='number']::-webkit-outer-spin-button { height: auto; } @@ -287,7 +289,7 @@ textarea { * 2. Correct the outline style in Safari. */ -[type="search"] { +[type='search'] { -webkit-appearance: textfield; /* 1 */ outline-offset: -2px; /* 2 */ } @@ -296,7 +298,7 @@ textarea { * Remove the inner padding in Chrome and Safari on macOS. */ -[type="search"]::-webkit-search-decoration { +[type='search']::-webkit-search-decoration { -webkit-appearance: none; }