Procházet zdrojové kódy

Implement cypress

This commit adds Cypress for testing the library against a real DOM.
master
TheoryOfNekomata před 3 roky
rodič
revize
dcda8ddfb2
19 změnil soubory, kde provedl 936 přidání a 139 odebrání
  1. +0
    -103
      README.md
  2. +6
    -0
      cypress.json
  3. +8
    -0
      cypress/tsconfig.json
  4. +2
    -0
      package.json
  5. +1
    -1
      src/index.test.ts
  6. +52
    -11
      src/index.ts
  7. +14
    -0
      test/fixtures/templates/default.html
  8. +29
    -0
      test/integrations/blank.e2e.ts
  9. +49
    -0
      test/integrations/default.e2e.ts
  10. +28
    -0
      test/integrations/single-disabled-input.e2e.ts
  11. +28
    -0
      test/integrations/single-input-with-double-input-submitters.e2e.ts
  12. +28
    -0
      test/integrations/single-input-with-double-submitters.e2e.ts
  13. +28
    -0
      test/integrations/single-input.e2e.ts
  14. +28
    -0
      test/integrations/single-readonly-input.e2e.ts
  15. +22
    -0
      test/plugins/index.ts
  16. +25
    -0
      test/support/commands.ts
  17. +20
    -0
      test/support/index.ts
  18. +0
    -0
      test/utils/index.ts
  19. +568
    -24
      yarn.lock

+ 0
- 103
README.md Zobrazit soubor

@@ -1,103 +0,0 @@
# TSDX User Guide

Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and how to use it.

> This TSDX setup is meant for developing libraries (not apps!) that can be published to NPM. If you’re looking to build a Node app, you could use `ts-node-dev`, plain `ts-node`, or simple `tsc`.

> If you’re new to TypeScript, checkout [this handy cheatsheet](https://devhints.io/typescript)

## Commands

TSDX scaffolds your new library inside `/src`.

To run TSDX, use:

```bash
npm start # or yarn start
```

This builds to `/dist` and runs the project in watch mode so any edits you save inside `src` causes a rebuild to `/dist`.

To do a one-off build, use `npm run build` or `yarn build`.

To run tests, use `npm test` or `yarn test`.

## Configuration

Code quality is set up for you with `prettier`, `husky`, and `lint-staged`. Adjust the respective fields in `package.json` accordingly.

### Jest

Jest tests are set up to run with `npm test` or `yarn test`.

### Bundle Analysis

[`size-limit`](https://github.com/ai/size-limit) is set up to calculate the real cost of your library with `npm run size` and visualize the bundle with `npm run analyze`.

#### Setup Files

This is the folder structure we set up for you:

```txt
/src
index.tsx # EDIT THIS
/test
blah.test.tsx # EDIT THIS
.gitignore
package.json
README.md # EDIT THIS
tsconfig.json
```

### Rollup

TSDX uses [Rollup](https://rollupjs.org) as a bundler and generates multiple rollup configs for various module formats and build settings. See [Optimizations](#optimizations) for details.

### TypeScript

`tsconfig.json` is set up to interpret `dom` and `esnext` types, as well as `react` for `jsx`. Adjust according to your needs.

## Continuous Integration

### GitHub Actions

Two actions are added by default:

- `main` which installs deps w/ cache, lints, tests, and builds on all pushes against a Node and OS matrix
- `size` which comments cost comparison of your library on every pull request using [`size-limit`](https://github.com/ai/size-limit)

## Optimizations

Please see the main `tsdx` [optimizations docs](https://github.com/palmerhq/tsdx#optimizations). In particular, know that you can take advantage of development-only optimizations:

```js
// ./types/index.d.ts
declare var __DEV__: boolean;

// inside your code...
if (__DEV__) {
console.log('foo');
}
```

You can also choose to install and use [invariant](https://github.com/palmerhq/tsdx#invariant) and [warning](https://github.com/palmerhq/tsdx#warning) functions.

## Module Formats

CJS, ESModules, and UMD module formats are supported.

The appropriate paths are configured in `package.json` and `dist/index.js` accordingly. Please report if any issues are found.

## Named Exports

Per Palmer Group guidelines, [always use named exports.](https://github.com/palmerhq/typescript#exports) Code split inside your React app instead of your React library.

## Including Styles

There are many ways to ship styles, including with CSS-in-JS. TSDX has no opinion on this, configure how you like.

For vanilla CSS, you can include it at the root directory and add it to the `files` section in your `package.json`, so that it can be imported separately by your users and run through their bundler's loader.

## Publishing to NPM

We recommend using [np](https://github.com/sindresorhus/np).

+ 6
- 0
cypress.json Zobrazit soubor

@@ -0,0 +1,6 @@
{
"fixturesFolder": "test/fixtures",
"integrationFolder": "test/integrations",
"pluginsFile": "test/plugins/index.ts",
"supportFile": "test/support/index.ts"
}

+ 8
- 0
cypress/tsconfig.json Zobrazit soubor

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
},
"include": ["**/*.ts"]
}

+ 2
- 0
package.json Zobrazit soubor

@@ -15,6 +15,7 @@
"build": "tsdx build",
"test": "tsdx test",
"lint": "tsdx lint",
"e2e": "cypress open",
"prepare": "tsdx build",
"size": "size-limit",
"analyze": "size-limit --why"
@@ -47,6 +48,7 @@
"devDependencies": {
"@size-limit/preset-small-lib": "^4.10.2",
"@types/jsdom": "^16.2.10",
"cypress": "^7.2.0",
"husky": "^6.0.0",
"jsdom": "^16.5.3",
"size-limit": "^4.10.2",


+ 1
- 1
src/index.test.ts Zobrazit soubor

@@ -1,4 +1,4 @@
import * as fixtures from '../test/fixtures'
import * as fixtures from '../test/utils'
import getFormValues from '.'

describe('blank template', () => {


+ 52
- 11
src/index.ts Zobrazit soubor

@@ -109,20 +109,37 @@ const getFieldValue = (el: FieldNode, submitter?: HTMLSubmitterElement) => {
const fieldEl = el as HTMLFieldElement
const tagName = fieldEl.tagName
const type = fieldEl.type

if (tagName === 'TEXTAREA') {
return fieldEl.value.replace(/\n/g, '\r\n')
}

if (tagName === 'SELECT' && fieldEl.value === '') {
return null
}

if (tagName === 'INPUT' && type === 'checkbox') {
const inputFieldEl = fieldEl as HTMLInputElement
const checkedValue = inputFieldEl.getAttribute('value')
if (checkedValue !== null) {
if (inputFieldEl.checked) {
return inputFieldEl.value
if (tagName === 'INPUT') {
switch (type) {
case 'checkbox':
const checkboxEl = fieldEl as HTMLInputElement
const checkedValue = checkboxEl.getAttribute('value')
if (checkedValue !== null) {
if (checkboxEl.checked) {
return checkboxEl.value
}
return null
}
return 'on' // default value
case 'radio':
const radioEl = fieldEl as HTMLInputElement
if (radioEl.checked) {
return radioEl.value
}
return null
default:
break
}
return inputFieldEl.checked
}

return fieldEl.value
@@ -154,15 +171,39 @@ const getFormValues = (form: HTMLFormElement, submitter?: HTMLSubmitterElement)
const formFieldElements = allFormFieldElements.filter(([, el]) => isValidFormField(el))
const fieldValues = formFieldElements.reduce(
(theFormValues, [,el]) => {
const inputEl = el as HTMLInputElement
if (inputEl.tagName === 'INPUT' && inputEl.type === 'radio' && !inputEl.checked) {
return theFormValues
}

const fieldValue = getFieldValue(el, submitter)
if (fieldValue === null) {
return theFormValues
}
return {
[el['name'] as string]: fieldValue,
}

const fieldName = el['name'] as string;
// const { [fieldName]: oldFormValue = null } = theFormValues;

// if (oldFormValue === null) {
return {
...theFormValues,
[fieldName]: fieldValue,
}
// }
//
// if (!Array.isArray(oldFormValue)) {
// return {
// ...theFormValues,
// [fieldName]: [oldFormValue, fieldValue],
// }
// }
//
// return {
// ...theFormValues,
// [fieldName]: [...oldFormValue, fieldValue],
// }
},
{}
{} as any
)
if (Boolean(submitter as unknown)) {
return {


+ 14
- 0
test/fixtures/templates/default.html Zobrazit soubor

@@ -80,5 +80,19 @@
</div>
</form>
</article>
<script>
Array.from(document.getElementsByClassName('dependents')).forEach(d => {
d.addEventListener('click', e => {
const container = document.createElement('div')
const input = document.createElement('input')
input.name = 'dependent'
input.type = 'text'
input.placeholder = 'Dependent'
container.classList.add('additional-dependent')
container.appendChild(input)
e.target.parentElement.parentElement.insertBefore(container, e.target.parentElement)
})
})
</script>
</body>
</html>

+ 29
- 0
test/integrations/blank.e2e.ts Zobrazit soubor

@@ -0,0 +1,29 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('blank template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/blank.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/blank.html' }).as('submitted');
})

it('should have blank form value', () => {
let beforeValues;
cy
.visit('/')
.get('form')
.then((formResult) => {
const [form] = Array.from(formResult);
beforeValues = getFormValues(form);
form.submit();
cy.wait('@submitted')
cy.location('search').then(search => {
console.log(beforeValues)
const before = new URLSearchParams(beforeValues).toString();
const after = new URLSearchParams(search).toString();
expect(before).to.equal(after);
})
})
});
})

+ 49
- 0
test/integrations/default.e2e.ts Zobrazit soubor

@@ -0,0 +1,49 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('single input template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/default.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/default.html' }).as('submitted');
})

it('should have a single form value', () => {
let beforeValues;
cy
.visit('/')
.then(() => {
cy.get('[name="first_name"]').type('John')
cy.get('[name="middle_name"]').type('Marcelo')
cy.get('[name="last_name"]').type('Dela Cruz')
cy.get('[name="gender"][value="m"]').check()
cy.get('[name="civil_status"]').select('Married')
cy.get('[name="new_registration"]').check()
cy.get('[name="nationality"][value="filipino"]').check()
cy.get('[name="dependent"][value="Jun"]').check()
// cy.get('button.dependents').click()
// cy.get('.additional-dependent [name="dependent"][type="text"]').eq(0).type('Juana')
// cy.get('button.dependents').click()
// cy.get('.additional-dependent [name="dependent"][type="text"]').eq(1).type('Jane')
// cy.get('button.dependents').click()
// cy.get('.additional-dependent [name="dependent"][type="text"]').eq(2).type('Josh')
cy.get('[name="notes"]').type('Test content\n\nNew line\n\nAnother line').as('filled')
})
.get('form')
.then((theForm) => {
cy
.get('[name="submit"][value="Hi"]')
.then((submitterEl) => {
const [submitter] = Array.from(submitterEl) as HTMLButtonElement[];
beforeValues = getFormValues(theForm[0], submitter);
submitterEl.trigger('click');
cy.wait('@submitted')
cy.location('search').then(search => {
const before = JSON.stringify(new URLSearchParams(beforeValues).toString().split('&'));
const after = JSON.stringify(new URLSearchParams(search).toString().split('&'));
expect(before).to.equal(after);
})
})
})
})
});

+ 28
- 0
test/integrations/single-disabled-input.e2e.ts Zobrazit soubor

@@ -0,0 +1,28 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('single input template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/single-disabled-input.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/single-disabled-input.html' }).as('submitted');
})

it('should have a single form value', () => {
let beforeValues;
cy
.visit('/')
.get('form')
.then((formResult) => {
const [form] = Array.from(formResult);
beforeValues = getFormValues(form);
form.submit();
cy.wait('@submitted')
cy.location('search').then(search => {
const before = new URLSearchParams(beforeValues).toString();
const after = new URLSearchParams(search).toString();
expect(before).to.equal(after);
})
})
})
});

+ 28
- 0
test/integrations/single-input-with-double-input-submitters.e2e.ts Zobrazit soubor

@@ -0,0 +1,28 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('single input template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/single-input-with-double-input-submitters.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/single-input-with-double-input-submitters.html' }).as('submitted');
})

it('should have a single form value', () => {
let beforeValues;
cy
.visit('/')
.get('form')
.then((formResult) => {
const [form] = Array.from(formResult);
beforeValues = getFormValues(form);
form.submit();
cy.wait('@submitted')
cy.location('search').then(search => {
const before = new URLSearchParams(beforeValues).toString();
const after = new URLSearchParams(search).toString();
expect(before).to.equal(after);
})
})
})
});

+ 28
- 0
test/integrations/single-input-with-double-submitters.e2e.ts Zobrazit soubor

@@ -0,0 +1,28 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('single input template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/single-input-with-double-submitters.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/single-input-with-double-submitters.html' }).as('submitted');
})

it('should have a single form value', () => {
let beforeValues;
cy
.visit('/')
.get('form')
.then((formResult) => {
const [form] = Array.from(formResult);
beforeValues = getFormValues(form);
form.submit();
cy.wait('@submitted')
cy.location('search').then(search => {
const before = new URLSearchParams(beforeValues).toString();
const after = new URLSearchParams(search).toString();
expect(before).to.equal(after);
})
})
})
});

+ 28
- 0
test/integrations/single-input.e2e.ts Zobrazit soubor

@@ -0,0 +1,28 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('single input template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/single-input.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/single-input.html' }).as('submitted');
})

it('should have a single form value', () => {
let beforeValues;
cy
.visit('/')
.get('form')
.then((formResult) => {
const [form] = Array.from(formResult);
beforeValues = getFormValues(form);
form.submit();
cy.wait('@submitted')
cy.location('search').then(search => {
const before = new URLSearchParams(beforeValues).toString();
const after = new URLSearchParams(search).toString();
expect(before).to.equal(after);
})
})
})
});

+ 28
- 0
test/integrations/single-readonly-input.e2e.ts Zobrazit soubor

@@ -0,0 +1,28 @@
/// <reference types="cypress" />

import getFormValues from '../../src'

describe('single input template', () => {
beforeEach(() => {
cy.intercept({ url: '/' }, { fixture: 'templates/single-readonly-input.html' });
cy.intercept({ url: '/?*' }, { fixture: 'templates/single-readonly-input.html' }).as('submitted');
})

it('should have a single form value', () => {
let beforeValues;
cy
.visit('/')
.get('form')
.then((formResult) => {
const [form] = Array.from(formResult);
beforeValues = getFormValues(form);
form.submit();
cy.wait('@submitted')
cy.location('search').then(search => {
const before = new URLSearchParams(beforeValues).toString();
const after = new URLSearchParams(search).toString();
expect(before).to.equal(after);
})
})
})
});

+ 22
- 0
test/plugins/index.ts Zobrazit soubor

@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.ts can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

+ 25
- 0
test/support/commands.ts Zobrazit soubor

@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

+ 20
- 0
test/support/index.ts Zobrazit soubor

@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.ts using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')

test/fixtures/index.ts → test/utils/index.ts Zobrazit soubor


+ 568
- 24
yarn.lock
Diff nebyl zobrazen, protože je příliš veliký
Zobrazit soubor


Načítá se…
Zrušit
Uložit