Browse Source

Restructure tests, add other scenarios

Handle selects, as well as file uploads.
master
TheoryOfNekomata 3 years ago
parent
commit
118147836f
28 changed files with 402 additions and 138 deletions
  1. +4
    -0
      README.md
  2. +1
    -6
      cypress.json
  3. +0
    -0
      cypress/fixtures/templates/blank.html
  4. +0
    -0
      cypress/fixtures/templates/everything.html
  5. +0
    -0
      cypress/fixtures/templates/single-disabled-input.html
  6. +16
    -0
      cypress/fixtures/templates/single-file-input.html
  7. +0
    -0
      cypress/fixtures/templates/single-input-with-double-button-submitters.html
  8. +0
    -0
      cypress/fixtures/templates/single-input-with-double-input-submitters.html
  9. +0
    -0
      cypress/fixtures/templates/single-input.html
  10. +16
    -0
      cypress/fixtures/templates/single-multiple-file-input.html
  11. +21
    -0
      cypress/fixtures/templates/single-multiple-select.html
  12. +15
    -0
      cypress/fixtures/templates/single-outside-input-and-submitter.html
  13. +18
    -0
      cypress/fixtures/templates/single-outside-input.html
  14. +0
    -0
      cypress/fixtures/templates/single-readonly-input.html
  15. +20
    -0
      cypress/fixtures/templates/single-select.html
  16. +3
    -0
      cypress/fixtures/uploads/data.json
  17. +2
    -5
      cypress/integration/blank.test.ts
  18. +2
    -5
      cypress/integration/everything.test.ts
  19. +200
    -0
      cypress/integration/single.test.ts
  20. +0
    -0
      cypress/plugins/index.ts
  21. +3
    -0
      cypress/support/commands.ts
  22. +0
    -1
      cypress/support/index.ts
  23. +1
    -1
      cypress/tsconfig.json
  24. +21
    -20
      package.json
  25. +54
    -6
      src/index.ts
  26. +0
    -94
      test/integration/single.test.ts
  27. +0
    -0
      test/utils/search.ts
  28. +5
    -0
      yarn.lock

+ 4
- 0
README.md View File

@@ -2,6 +2,10 @@

Extract form values through the DOM.

## Motivation

There are many ways to lay out forms.

## Installation

The library is not yet out on any package manager. Installing through the URL is the preferred way.


+ 1
- 6
cypress.json View File

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

test/fixtures/templates/blank.html → cypress/fixtures/templates/blank.html View File


test/fixtures/templates/everything.html → cypress/fixtures/templates/everything.html View File


test/fixtures/templates/single-disabled-input.html → cypress/fixtures/templates/single-disabled-input.html View File


+ 16
- 0
cypress/fixtures/templates/single-file-input.html View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="file" name="hello" />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

test/fixtures/templates/single-input-with-double-button-submitters.html → cypress/fixtures/templates/single-input-with-double-button-submitters.html View File


test/fixtures/templates/single-input-with-double-input-submitters.html → cypress/fixtures/templates/single-input-with-double-input-submitters.html View File


test/fixtures/templates/single-input.html → cypress/fixtures/templates/single-input.html View File


+ 16
- 0
cypress/fixtures/templates/single-multiple-file-input.html View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="file" name="hello" multiple />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 21
- 0
cypress/fixtures/templates/single-multiple-select.html View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<select name="hello" multiple>
<option>Foo</option>
<option selected>Bar</option>
<option>Baz</option>
<option selected>Quux</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 15
- 0
cypress/fixtures/templates/single-outside-input-and-submitter.html View File

@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="form"></form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" form="form" />
</label>
<button type="submit" form="form">Submit</button>
</body>
</html>

+ 18
- 0
cypress/fixtures/templates/single-outside-input.html View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form
id="form"
>
<button type="submit">Submit</button>
</form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" form="form" />
</label>
</body>
</html>

test/fixtures/templates/single-readonly-input.html → cypress/fixtures/templates/single-readonly-input.html View File


+ 20
- 0
cypress/fixtures/templates/single-select.html View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<select name="hello">
<option>Foo</option>
<option>Bar</option>
<option selected>Baz</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 3
- 0
cypress/fixtures/uploads/data.json View File

@@ -0,0 +1,3 @@
{
"hello": "hi"
}

test/integration/blank.test.ts → cypress/integration/blank.test.ts View File

@@ -1,8 +1,5 @@
/// <reference types="cypress" />
/// <reference types="cypress-jest-adapter" />

import getFormValues from '../../src'
import * as utils from '../utils'
import * as utils from '../../test/utils'

describe('blank template', () => {
beforeEach(utils.setup('blank'))
@@ -11,7 +8,7 @@ describe('blank template', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},

test/integration/everything.test.ts → cypress/integration/everything.test.ts View File

@@ -1,8 +1,5 @@
/// <reference types="cypress" />
/// <reference types="cypress-jest-adapter" />

import getFormValues from '../../src'
import * as utils from '../utils';
import * as utils from '../../test/utils';

describe('blank template', () => {
beforeEach(utils.setup('everything'))
@@ -31,7 +28,7 @@ describe('blank template', () => {
return cy.get('[name="submit"][value="Hi"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},

+ 200
- 0
cypress/integration/single.test.ts View File

@@ -0,0 +1,200 @@
import getFormValues from '../../src'
import * as utils from '../../test/utils';

describe('single input template', () => {
beforeEach(utils.setup('single-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single outside input template', () => {
beforeEach(utils.setup('single-outside-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single outside input and submitter template', () => {
beforeEach(utils.setup('single-outside-input-and-submitter'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single readonly template', () => {
beforeEach(utils.setup('single-readonly-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single disabled template', () => {
beforeEach(utils.setup('single-disabled-input'))

it('should have blank form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
});
})

describe('single input with double button submitters template', () => {
beforeEach(utils.setup('single-input-with-double-button-submitters'))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Foo"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Foo',
}
);
});
})

describe('single input with double input submitters template', () => {
beforeEach(utils.setup('single-input-with-double-input-submitters'))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Bar"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Bar',
}
);
});
})

describe('single select template', () => {
beforeEach(utils.setup('single-select'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Baz',
}
);
});
})

describe('single multiple select template', () => {
beforeEach(utils.setup('single-multiple-select'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Bar,Quux',
}
);
});
})

describe('single file input template', () => {
beforeEach(utils.setup('single-file-input'))

it('should have no form values when no file is selected', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
})

it('should have single form value when a file is selected', () => {
utils.test(
(cy: any) => {
cy
.get('[name="hello"]')
.attachFile('uploads/data.json')

return cy.get('[type="submit"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'data.json',
}
);
})
})

test/plugins/index.ts → cypress/plugins/index.ts View File


test/support/commands.ts → cypress/support/commands.ts View File

@@ -23,3 +23,6 @@
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

import 'cypress-jest-adapter'
import 'cypress-file-upload'

test/support/index.ts → cypress/support/index.ts View File

@@ -15,7 +15,6 @@

// Import commands.ts using ES2015 syntax:
import './commands'
import 'cypress-jest-adapter'

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

+ 1
- 1
cypress/tsconfig.json View File

@@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
"types": ["cypress", "cypress-file-upload", "cypress-jest-adapter"]
},
"include": ["**/*.ts"]
}

+ 21
- 20
package.json View File

@@ -1,5 +1,5 @@
{
"version": "0.1.0",
"version": "0.2.0",
"license": "MIT",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
@@ -7,25 +7,25 @@
"dist",
"src"
],
"publishing": {
"github": {
"repository": "https://github.com/TheoryOfNekomata/formxtr.git",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
},
"master": {
"repository": "https://code.modal.sh/TheoryOfNekomata/formxtr.git",
"publishConfig": {
"registry": "https://js.pack.modal.sh"
}
},
"npm": {
"publishConfig": {
"registry": "https://registry.npmjs.com"
}
}
},
"publishing": {
"github": {
"repository": "https://github.com/TheoryOfNekomata/formxtr.git",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
},
"master": {
"repository": "https://code.modal.sh/TheoryOfNekomata/formxtr.git",
"publishConfig": {
"registry": "https://js.pack.modal.sh"
}
},
"npm": {
"publishConfig": {
"registry": "https://registry.npmjs.com"
}
}
},
"engines": {
"node": ">=10"
},
@@ -68,6 +68,7 @@
"@size-limit/preset-small-lib": "^4.10.2",
"@types/jsdom": "^16.2.10",
"cypress": "^7.2.0",
"cypress-file-upload": "^5.0.7",
"cypress-jest-adapter": "^0.1.1",
"husky": "^6.0.0",
"jsdom": "^16.5.3",


+ 54
- 6
src/index.ts View File

@@ -1,9 +1,20 @@
/**
* Type for valid field elements.
*
*
*/

type HTMLFieldElement
= HTMLInputElement
| HTMLButtonElement
| HTMLSelectElement
| HTMLTextAreaElement

/**
* Type for valid submitter elements.
*
* Only the <button> and <input> elements can be submitter elements.
*/
type HTMLSubmitterElement
= HTMLButtonElement
| HTMLInputElement
@@ -42,8 +53,15 @@ const getFieldValue = (el: HTMLFieldElement) => {
return fieldEl.value.replace(/\n/g, '\r\n')
}

if (tagName === 'SELECT' && fieldEl.value === '') {
return null
if (tagName === 'SELECT') {
if (fieldEl.value === '') {
return null
}
const selectEl = fieldEl as HTMLSelectElement
if (selectEl.multiple) {
return Array.from(selectEl.options).filter(o => o.selected).map(o => o.value)
}
return selectEl.value
}

if (tagName === 'INPUT') {
@@ -64,6 +82,17 @@ const getFieldValue = (el: HTMLFieldElement) => {
return radioEl.value
}
return null
case 'file':
const fileUploadEl = fieldEl as HTMLInputElement
const { files } = fileUploadEl
if ((files as unknown) !== null) {
const [file = null] = Array.from(files as FileList)
if (file !== null) {
return file.name
}
return ''
}
return null
default:
break
}
@@ -75,7 +104,7 @@ const getFieldValue = (el: HTMLFieldElement) => {

/**
* Returns only named form field elements.
* @param el - The element
* @param el - The element.
*/
const isValidFormField = (el: HTMLFieldElement) => {
return (
@@ -87,10 +116,29 @@ const isValidFormField = (el: HTMLFieldElement) => {
)
}

type GetFormValuesOptions = {
/**
* The element that triggered the submission of the form.
*/
submitter?: HTMLSubmitterElement,
/**
* Should we consider the `checked` attribute of checkboxes with no `value` attributes instead of the default value
* "on" when checked?
*/
booleanValuelessCheckbox?: true,
/**
* Should we retrieve the `files` attribute of file inputs instead of the currently selected file names?
*/
hasFiles?: true,
}

/**
* Gets the values of all the fields within the form through accessing the DOM nodes.
* @param form - The form.
* @param options - The options.
* @returns The form values.
*/
const getFormValues = (form: HTMLFormElement, submitter?: HTMLSubmitterElement) => {
const getFormValues = (form: HTMLFormElement, options = {} as GetFormValuesOptions) => {
if (!form) {
throw new TypeError('Invalid form element.')
}
@@ -134,10 +182,10 @@ const getFormValues = (form: HTMLFormElement, submitter?: HTMLSubmitterElement)
},
{} as any
)
if (Boolean(submitter as unknown)) {
if (Boolean(options.submitter as unknown)) {
return {
...fieldValues,
[(submitter as HTMLSubmitterElement).name]: (submitter as HTMLSubmitterElement).value,
[(options.submitter as HTMLSubmitterElement).name]: (options.submitter as HTMLSubmitterElement).value,
}
}
return fieldValues


+ 0
- 94
test/integration/single.test.ts View File

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

import getFormValues from '../../src'
import * as utils from '../utils';

describe('single input template', () => {
beforeEach(utils.setup('single-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single readonly template', () => {
beforeEach(utils.setup('single-readonly-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single disabled template', () => {
beforeEach(utils.setup('single-disabled-input'))

it('should have blank form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
});
})

describe('single input with double button submitters template', () => {
beforeEach(utils.setup('single-input-with-double-button-submitters'))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Foo"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Foo',
}
);
});
})

describe('single input with double input submitters template', () => {
beforeEach(utils.setup('single-input-with-double-input-submitters'))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Bar"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Bar',
}
);
});
})

+ 0
- 0
test/utils/search.ts View File


+ 5
- 0
yarn.lock View File

@@ -3182,6 +3182,11 @@ cyclist@^1.0.1:
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
cypress-file-upload@^5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.7.tgz#acf24fe08a92b2d0c892a58b56811fb933d34ea9"
integrity sha512-cgWsWx7igxjyyVm9/VJ9ukdy69jL00I7z0lrwUWtXXLPvX4neO+8JAZ054Ax8Xf+mdV9OerenXzb9nqRoafjHA==
cypress-jest-adapter@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/cypress-jest-adapter/-/cypress-jest-adapter-0.1.1.tgz#d1aa9d84393b6a5007022d1d33b3cdd3ce9672af"


Loading…
Cancel
Save