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. Extract form values through the DOM.


## Motivation

There are many ways to lay out forms.

## Installation ## Installation


The library is not yet out on any package manager. Installing through the URL is the preferred way. 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 getFormValues from '../../src'
import * as utils from '../utils'
import * as utils from '../../test/utils'


describe('blank template', () => { describe('blank template', () => {
beforeEach(utils.setup('blank')) beforeEach(utils.setup('blank'))
@@ -11,7 +8,7 @@ describe('blank template', () => {
utils.test( utils.test(
(cy: any) => cy.get('[type="submit"]'), (cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => { (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(); const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after); 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 getFormValues from '../../src'
import * as utils from '../utils';
import * as utils from '../../test/utils';


describe('blank template', () => { describe('blank template', () => {
beforeEach(utils.setup('everything')) beforeEach(utils.setup('everything'))
@@ -31,7 +28,7 @@ describe('blank template', () => {
return cy.get('[name="submit"][value="Hi"]') return cy.get('[name="submit"][value="Hi"]')
}, },
(form: HTMLFormElement, submitter: any, search: any) => { (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(); const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after); 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 -- // -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) // 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.ts using ES2015 syntax:
import './commands' import './commands'
import 'cypress-jest-adapter'


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

+ 1
- 1
cypress/tsconfig.json View File

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

+ 21
- 20
package.json View File

@@ -1,5 +1,5 @@
{ {
"version": "0.1.0",
"version": "0.2.0",
"license": "MIT", "license": "MIT",
"main": "dist/index.js", "main": "dist/index.js",
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
@@ -7,25 +7,25 @@
"dist", "dist",
"src" "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": { "engines": {
"node": ">=10" "node": ">=10"
}, },
@@ -68,6 +68,7 @@
"@size-limit/preset-small-lib": "^4.10.2", "@size-limit/preset-small-lib": "^4.10.2",
"@types/jsdom": "^16.2.10", "@types/jsdom": "^16.2.10",
"cypress": "^7.2.0", "cypress": "^7.2.0",
"cypress-file-upload": "^5.0.7",
"cypress-jest-adapter": "^0.1.1", "cypress-jest-adapter": "^0.1.1",
"husky": "^6.0.0", "husky": "^6.0.0",
"jsdom": "^16.5.3", "jsdom": "^16.5.3",


+ 54
- 6
src/index.ts View File

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

type HTMLFieldElement type HTMLFieldElement
= HTMLInputElement = HTMLInputElement
| HTMLButtonElement | HTMLButtonElement
| HTMLSelectElement | HTMLSelectElement
| HTMLTextAreaElement | HTMLTextAreaElement


/**
* Type for valid submitter elements.
*
* Only the <button> and <input> elements can be submitter elements.
*/
type HTMLSubmitterElement type HTMLSubmitterElement
= HTMLButtonElement = HTMLButtonElement
| HTMLInputElement | HTMLInputElement
@@ -42,8 +53,15 @@ const getFieldValue = (el: HTMLFieldElement) => {
return fieldEl.value.replace(/\n/g, '\r\n') 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') { if (tagName === 'INPUT') {
@@ -64,6 +82,17 @@ const getFieldValue = (el: HTMLFieldElement) => {
return radioEl.value return radioEl.value
} }
return null 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: default:
break break
} }
@@ -75,7 +104,7 @@ const getFieldValue = (el: HTMLFieldElement) => {


/** /**
* Returns only named form field elements. * Returns only named form field elements.
* @param el - The element
* @param el - The element.
*/ */
const isValidFormField = (el: HTMLFieldElement) => { const isValidFormField = (el: HTMLFieldElement) => {
return ( 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. * 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) { if (!form) {
throw new TypeError('Invalid form element.') throw new TypeError('Invalid form element.')
} }
@@ -134,10 +182,10 @@ const getFormValues = (form: HTMLFormElement, submitter?: HTMLSubmitterElement)
}, },
{} as any {} as any
) )
if (Boolean(submitter as unknown)) {
if (Boolean(options.submitter as unknown)) {
return { return {
...fieldValues, ...fieldValues,
[(submitter as HTMLSubmitterElement).name]: (submitter as HTMLSubmitterElement).value,
[(options.submitter as HTMLSubmitterElement).name]: (options.submitter as HTMLSubmitterElement).value,
} }
} }
return fieldValues 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" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9"
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= 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: cypress-jest-adapter@^0.1.1:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/cypress-jest-adapter/-/cypress-jest-adapter-0.1.1.tgz#d1aa9d84393b6a5007022d1d33b3cdd3ce9672af" resolved "https://registry.yarnpkg.com/cypress-jest-adapter/-/cypress-jest-adapter-0.1.1.tgz#d1aa9d84393b6a5007022d1d33b3cdd3ce9672af"


Loading…
Cancel
Save