Include all tests for the primary functions as well as the utilities.master
@@ -55,7 +55,7 @@ describe('checkbox', () => { | |||
} | |||
}); | |||
}); | |||
}) | |||
}); | |||
describe('checked', () => { | |||
beforeEach(utils.setup(` | |||
@@ -93,7 +93,6 @@ describe('checkbox', () => { | |||
}); | |||
}); | |||
describe('duplicate', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
@@ -165,4 +164,221 @@ describe('checkbox', () => { | |||
}); | |||
}); | |||
}); | |||
describe('setting values', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Checkbox/Setting Values</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello</span> | |||
<input type="checkbox" name="enabled" /> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should check for boolean "true"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: true, }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).toBe('on'); | |||
}, | |||
expectedStaticValue: 'enabled=on', | |||
}); | |||
}); | |||
it('should check for string "true"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'true', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).toBe('on'); | |||
}, | |||
expectedStaticValue: 'enabled=on', | |||
}); | |||
}); | |||
it('should check for string "yes"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'yes', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).toBe('on'); | |||
}, | |||
expectedStaticValue: 'enabled=on', | |||
}); | |||
}); | |||
it('should check for string "on"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'on', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).toBe('on'); | |||
}, | |||
expectedStaticValue: 'enabled=on', | |||
}); | |||
}); | |||
it('should uncheck for boolean "false"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: false, }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should uncheck for string "false"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'false', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should uncheck for string "no"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'no', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should uncheck for string "off"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'off', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should check for number "1"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 1, }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).toBe('on'); | |||
}, | |||
expectedStaticValue: 'enabled=on', | |||
}); | |||
}); | |||
it('should check for string "1"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: '1', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).toBe('on'); | |||
}, | |||
expectedStaticValue: 'enabled=on', | |||
}); | |||
}); | |||
it('should uncheck for number "0"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 0, }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should uncheck for string "0"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: '0', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should uncheck for object "null"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: null, }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
it('should uncheck for string "null"', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { enabled: 'null', }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter }) | |||
expect(values['enabled']).not.toBe('on'); | |||
}, | |||
expectedStaticValue: '', | |||
}); | |||
}); | |||
}); | |||
}); |
@@ -38,6 +38,19 @@ describe('date', () => { | |||
}, | |||
}); | |||
}); | |||
it('should enable Date representation', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter, forceDateValues: true }); | |||
// somehow, checking instanceof Date fails here, because we're using an artificial date | |||
// object? | |||
const testDate = new Date(values.hello as Date); | |||
expect((values.hello as Date).getTime()).toBe(testDate.getTime()); | |||
}, | |||
}); | |||
}); | |||
}) | |||
describe('disabled', () => { | |||
@@ -38,6 +38,19 @@ describe('date', () => { | |||
}, | |||
}); | |||
}); | |||
it('should enable Date representation', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter, forceDateValues: true }); | |||
// somehow, checking instanceof Date fails here, because we're using an artificial date | |||
// object? | |||
const testDate = new Date(values.hello as Date); | |||
expect((values.hello as Date).getTime()).toBe(testDate.getTime()); | |||
}, | |||
}); | |||
}); | |||
}) | |||
describe('disabled', () => { | |||
@@ -1,4 +1,10 @@ | |||
import getFormValuesDeprecated, { getFormValues, setFormValues } from '../../src'; | |||
import getFormValuesDeprecated, { | |||
getFormValues, | |||
setFormValues, | |||
isFieldElement, | |||
isElementValueIncludedInFormSubmit, | |||
getValue, | |||
} from '../../src'; | |||
import * as utils from '../utils' | |||
describe('misc', () => { | |||
@@ -177,7 +183,213 @@ describe('misc', () => { | |||
}, | |||
}); | |||
}); | |||
}) | |||
}); | |||
describe('utilities', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Misc/Utilities</title> | |||
</head> | |||
<body> | |||
<form> | |||
<input id="input" type="text" name="foobar" /> | |||
<input id="notField" type="text" /> | |||
<input id="disabled" disabled type="text" name="disabled" /> | |||
<meter id="meter" min="1" max="10" value="5" /> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)); | |||
it('should check for valid field elements value', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const meter = document.getElementById('meter'); | |||
expect(getValue(meter)).toBe(5); | |||
}, | |||
}); | |||
}); | |||
it('should check for invalid field elements value', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
expect(getValue(document.body)).toBe(null); | |||
}, | |||
}); | |||
}); | |||
it('should check for elements as included fields', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const input = document.getElementById('input'); | |||
expect(isElementValueIncludedInFormSubmit(input)).toBe(true); | |||
}, | |||
}); | |||
}); | |||
it('should check for elements as excluded fields', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const notField = document.getElementById('notField'); | |||
expect(isElementValueIncludedInFormSubmit(notField)).toBe(false); | |||
const disabled = document.getElementById('disabled'); | |||
expect(isElementValueIncludedInFormSubmit(disabled)).toBe(false); | |||
const meter = document.getElementById('meter'); | |||
expect(isElementValueIncludedInFormSubmit(meter)).toBe(false); | |||
}, | |||
}); | |||
}); | |||
it('should check for elements as valid for fields', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const input = document.getElementById('input'); | |||
expect(isFieldElement(input)).toBe(true); | |||
const disabled = document.getElementById('disabled'); | |||
expect(isFieldElement(disabled)).toBe(true); | |||
}, | |||
}); | |||
}); | |||
it('should check for elements as invalid for fields', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const meter = document.getElementById('meter'); | |||
expect(isFieldElement(meter)).toBe(false); | |||
const notField = document.getElementById('notField'); | |||
expect(isFieldElement(notField)).toBe(false); | |||
}, | |||
}); | |||
}); | |||
}); | |||
describe('setting values', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Misc/Blank</title> | |||
</head> | |||
<body> | |||
<form> | |||
<input type="text" name="foobar" /> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should parse string values for setFormValues', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
let isThrown = false; | |||
try { | |||
setFormValues(form, 'foobar=baz'); | |||
} catch (e) { | |||
isThrown = true; | |||
} | |||
expect(isThrown).toBe(false); | |||
expect(getFormValues(form)).toEqual({ foobar: 'baz', }); | |||
}, | |||
}) | |||
}); | |||
it('should parse entries values for setFormValues', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
let isThrown = false; | |||
try { | |||
setFormValues(form, [['foobar', 'baz']]); | |||
} catch (e) { | |||
isThrown = true; | |||
} | |||
expect(isThrown).toBe(false); | |||
expect(getFormValues(form)).toEqual({ foobar: 'baz', }); | |||
}, | |||
}) | |||
}); | |||
it('should parse URLSearchParams values for setFormValues', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
let isThrown = false; | |||
try { | |||
setFormValues(form, new URLSearchParams('foobar=baz')); | |||
} catch (e) { | |||
isThrown = true; | |||
} | |||
expect(isThrown).toBe(false); | |||
expect(getFormValues(form)).toEqual({ foobar: 'baz', }); | |||
}, | |||
}) | |||
}); | |||
it('should parse object values for setFormValues', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
let isThrown = false; | |||
try { | |||
setFormValues(form, { foobar: 'baz', }); | |||
} catch (e) { | |||
isThrown = true; | |||
} | |||
expect(isThrown).toBe(false); | |||
expect(getFormValues(form)).toEqual({ foobar: 'baz', }); | |||
}, | |||
}) | |||
}); | |||
}); | |||
describe('duplicates', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Misc/Blank</title> | |||
</head> | |||
<body> | |||
<form> | |||
<input type="text" name="foobar" /> | |||
<input type="text" name="foobar" /> | |||
<input type="text" name="foobar" /> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)); | |||
it('should parse duplicates correctly', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { foobar: ['foo', 'bar', 'baz']}) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
expect(getFormValues(form)).toEqual({ foobar: ['foo', 'bar', 'baz'], }); | |||
}, | |||
}) | |||
}); | |||
}); | |||
describe('blank', () => { | |||
beforeEach(utils.setup(` | |||
@@ -38,6 +38,19 @@ describe('month', () => { | |||
}, | |||
}); | |||
}); | |||
it('should enable Date representation', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const values = getFormValues(form, { submitter, forceDateValues: true }); | |||
// somehow, checking instanceof Date fails here, because we're using an artificial date | |||
// object? | |||
const testDate = new Date(values.hello as Date); | |||
expect((values.hello as Date).getTime()).toBe(testDate.getTime()); | |||
}, | |||
}); | |||
}); | |||
}) | |||
describe('disabled', () => { | |||
@@ -1,4 +1,4 @@ | |||
import { getFormValues } from '../../src' | |||
import { getFormValues, setFormValues } from '../../src'; | |||
import * as utils from '../utils' | |||
describe('select', () => { | |||
@@ -41,6 +41,107 @@ describe('select', () => { | |||
expectedStaticValue: 'hello=Bar&hello=Quux' | |||
}); | |||
}); | |||
it('should set values correctly', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { hello: ['Foo', 'Baz'] }); | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Foo&hello=Baz' | |||
}); | |||
}); | |||
}) | |||
describe('multiple duplicate', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Select/Multiple Duplicate</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> | |||
<select name="hello" multiple> | |||
<option>Chocolate</option> | |||
<option selected>Mango</option> | |||
<option>Vanilla</option> | |||
<option selected>Ube</option> | |||
</select> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should have multiple form values on a single field', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Bar&hello=Quux&hello=Mango&hello=Ube' | |||
}); | |||
}); | |||
it('should set multiple form values across all selects', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { hello: ['Foo', 'Baz', 'Chocolate', 'Vanilla'] }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Foo&hello=Baz&hello=Chocolate&hello=Vanilla' | |||
}); | |||
}); | |||
it('should set multiple form values on each corresponding select element', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { hello: [['Foo', 'Baz', 'Chocolate'], ['Vanilla']] }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Foo&hello=Baz&hello=Vanilla' | |||
}); | |||
}); | |||
}) | |||
describe('single', () => { | |||
@@ -84,4 +185,88 @@ describe('select', () => { | |||
}); | |||
}); | |||
}) | |||
describe('single duplicate', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Select/Single Duplicate</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello</span> | |||
<select name="hello"> | |||
<option>Foo</option> | |||
<option selected>Bar</option> | |||
<option>Baz</option> | |||
<option>Quux</option> | |||
</select> | |||
<select name="hello"> | |||
<option>Chocolate</option> | |||
<option>Mango</option> | |||
<option>Vanilla</option> | |||
<option selected>Ube</option> | |||
<option>Foo</option> | |||
</select> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should have multiple form values on a single field', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Bar&hello=Ube' | |||
}); | |||
}); | |||
it('should set multiple form values across all selects', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { hello: ['Foo', 'Chocolate'] }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Foo&hello=Chocolate' | |||
}); | |||
}); | |||
it('should set multiple form values on each corresponding select element', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { hello: ['Foo', 'Ube'] }) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: 'hello=Foo&hello=Ube' | |||
}); | |||
}); | |||
}) | |||
}) |
@@ -0,0 +1,265 @@ | |||
import { getFormValues, setFormValues } from '../../src'; | |||
import * as utils from '../utils' | |||
describe('time', () => { | |||
describe('basic', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Time/Basic</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello</span> | |||
<input type="time" name="hello" value="13:37" /> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should have single form value', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: { | |||
hello: '13:37', | |||
}, | |||
}); | |||
}); | |||
}) | |||
describe('disabled', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Time/Disabled</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello</span> | |||
<input | |||
type="time" name="hello" value="13:37" | |||
disabled | |||
/> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should have blank form value', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: {}, | |||
}); | |||
}); | |||
}) | |||
describe('outside', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Time/Outside</title> | |||
</head> | |||
<body> | |||
<form id="form"> | |||
<button type="submit">Submit</button> | |||
</form> | |||
<label> | |||
<span>Hello</span> | |||
<input type="time" name="hello" value="13:37" form="form" /> | |||
</label> | |||
</body> | |||
</html> | |||
`)) | |||
it('should have single form value', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: { | |||
hello: '13:37', | |||
}, | |||
}); | |||
}); | |||
}); | |||
describe('readonly', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Time/Readonly</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello</span> | |||
<input | |||
type="time" name="hello" value="13:37" | |||
readonly | |||
/> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)) | |||
it('should have single form value', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: { | |||
hello: '13:37', | |||
}, | |||
}); | |||
}); | |||
}); | |||
describe('programmatic value setting', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Time/Programmatic Value Setting</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello</span> | |||
<input type="time" name="hello" /> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)); | |||
it('should have form values set', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { hello: '13:37', }) | |||
}, | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: { | |||
hello: '13:37', | |||
}, | |||
}); | |||
}); | |||
}); | |||
describe('duplicate', () => { | |||
beforeEach(utils.setup(` | |||
<!DOCTYPE html> | |||
<html lang="en-PH"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<title>Time/Duplicate</title> | |||
</head> | |||
<body> | |||
<form> | |||
<label> | |||
<span>Hello 1</span> | |||
<input id="hello1" type="time" value="13:37" name="hello"/> | |||
</label> | |||
<label> | |||
<span>Hello 2</span> | |||
<input id="hello2" type="time" value="06:09" name="hello"/> | |||
</label> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> | |||
`)); | |||
it('should get both values', () => { | |||
utils.test({ | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: { | |||
hello: ['13:37', '06:09'], | |||
}, | |||
}); | |||
}); | |||
it('should set both values', () => { | |||
utils.test({ | |||
preAction: (form: HTMLFormElement) => { | |||
setFormValues(form, { | |||
hello: ['04:20', '05:30'], | |||
}) | |||
}, | |||
action: (cy: any) => cy.get('[type="submit"]'), | |||
test: (form: HTMLFormElement, submitter: any, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, { submitter })) | |||
.toString(); | |||
const after = utils.makeSearchParams(search) | |||
.toString(); | |||
expect(before) | |||
.toEqual(after); | |||
}, | |||
expectedStaticValue: { | |||
hello: ['04:20', '05:30'], | |||
}, | |||
}); | |||
}); | |||
}); | |||
}) |
@@ -44,8 +44,9 @@ const FORM_FIELD_INPUT_EXCLUDED_TYPES = ['submit', 'reset'] as const; | |||
/** | |||
* Checks if an element can hold a custom (user-inputted) field value. | |||
* @param el - The element. | |||
* @returns Value determining if an element can hold a custom (user-inputted) field value. | |||
*/ | |||
export const isFormFieldElement = (el: HTMLElement) => { | |||
export const isFieldElement = (el: HTMLElement) => { | |||
const { tagName } = el; | |||
if (FORM_FIELD_ELEMENT_TAG_NAMES.includes(tagName as typeof FORM_FIELD_ELEMENT_TAG_NAMES[0])) { | |||
return true; | |||
@@ -92,15 +93,15 @@ const getTextAreaFieldValue = ( | |||
* @param textareaEl - The element. | |||
* @param value - Value of the textarea element. | |||
* @param nthOfName - What order is this field in with respect to fields of the same name? | |||
* @param totalOfName - How many fields with the same name are in the form? | |||
* @param elementsOfSameName - How many fields with the same name are in the form? | |||
*/ | |||
const setTextAreaFieldValue = ( | |||
textareaEl: HTMLTextAreaElement, | |||
value: unknown, | |||
nthOfName: number, | |||
totalOfName: number, | |||
elementsOfSameName: HTMLTextAreaElement[], | |||
) => { | |||
if (Array.isArray(value) && totalOfName > 1) { | |||
if (Array.isArray(value) && elementsOfSameName.length > 1) { | |||
// eslint-disable-next-line no-param-reassign | |||
textareaEl.value = value[nthOfName]; | |||
return; | |||
@@ -128,19 +129,54 @@ const getSelectFieldValue = ( | |||
* Sets the value of a `<select>` element. | |||
* @param selectEl - The element. | |||
* @param value - Value of the select element. | |||
* @param nthOfName - What order is this field in with respect to fields of the same name? | |||
* @param elementsOfSameName - How many fields with the same name are in the form? | |||
*/ | |||
const setSelectFieldValue = (selectEl: HTMLSelectElement, value: unknown) => { | |||
Array.from(selectEl.options) | |||
.filter((o) => { | |||
if (Array.isArray(value)) { | |||
return (value as string[]).includes(o.value); | |||
} | |||
return o.value === value; | |||
}) | |||
.forEach((el) => { | |||
const setSelectFieldValue = ( | |||
selectEl: HTMLSelectElement, | |||
value: unknown, | |||
nthOfName: number, | |||
elementsOfSameName: HTMLSelectElement[], | |||
) => { | |||
if (elementsOfSameName.length > 1) { | |||
const valueArray = value as unknown[]; | |||
const valueArrayDepth = valueArray.every((v) => Array.isArray(v)) ? 2 : 1; | |||
if (valueArrayDepth > 1) { | |||
// We check if values are [['foo', 'bar], ['baz', 'quux'], 'single value] | |||
// If this happens, all values must correspond to a <select multiple> element. | |||
const currentValue = valueArray[nthOfName] as string[]; | |||
Array.from(selectEl.options).forEach((el) => { | |||
// eslint-disable-next-line no-param-reassign | |||
el.selected = currentValue.includes(el.value); | |||
}); | |||
return; | |||
} | |||
// Else we're just checking if these values are in the value array provided. | |||
// They will apply across all select elements. | |||
if (elementsOfSameName.some((el) => el.multiple)) { | |||
Array.from(selectEl.options).forEach((el) => { | |||
// eslint-disable-next-line no-param-reassign | |||
el.selected = (value as string[]).includes(el.value); | |||
}); | |||
return; | |||
} | |||
Array.from(selectEl.options).forEach((el) => { | |||
// eslint-disable-next-line no-param-reassign | |||
el.selected = true; | |||
el.selected = el.value === (value as string[])[nthOfName]; | |||
}); | |||
return; | |||
} | |||
Array.from(selectEl.options).forEach((el) => { | |||
// eslint-disable-next-line no-param-reassign | |||
el.selected = Array.isArray(value) | |||
? (value as string[]).includes(el.value) | |||
: el.value === value; | |||
}); | |||
}; | |||
/** | |||
@@ -248,6 +284,45 @@ const getInputCheckboxFieldValue = ( | |||
return null; | |||
}; | |||
const parseBooleanValues = (value: unknown) => { | |||
if (typeof value === 'boolean') { | |||
return value; | |||
} | |||
if (typeof value === 'string') { | |||
const normalizedValue = value.toLowerCase(); | |||
if (INPUT_CHECKBOX_FALSY_VALUES.includes( | |||
normalizedValue as typeof INPUT_CHECKBOX_FALSY_VALUES[0], | |||
)) { | |||
return false; | |||
} | |||
if (INPUT_CHECKBOX_TRUTHY_VALUES.includes( | |||
normalizedValue as typeof INPUT_CHECKBOX_TRUTHY_VALUES[0], | |||
)) { | |||
return true; | |||
} | |||
} | |||
if (typeof value === 'number') { | |||
if (value === 0) { | |||
return false; | |||
} | |||
if (value === 1) { | |||
return true; | |||
} | |||
} | |||
if (typeof value === 'object') { | |||
if (value === null) { | |||
return false; | |||
} | |||
} | |||
return undefined; | |||
}; | |||
/** | |||
* Sets the value of an `<input type="checkbox">` element. | |||
* @param inputEl - The element. | |||
@@ -269,26 +344,10 @@ const setInputCheckboxFieldValue = ( | |||
return; | |||
} | |||
if ( | |||
INPUT_CHECKBOX_FALSY_VALUES.includes( | |||
(value as string).toLowerCase() as typeof INPUT_CHECKBOX_FALSY_VALUES[0], | |||
) | |||
|| !value | |||
) { | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.checked = false; | |||
return; | |||
} | |||
if ( | |||
INPUT_CHECKBOX_TRUTHY_VALUES.includes( | |||
(value as string).toLowerCase() as typeof INPUT_CHECKBOX_TRUTHY_VALUES[0], | |||
) | |||
|| value === true | |||
|| value === 1 | |||
) { | |||
const newValue = parseBooleanValues(value); | |||
if (typeof newValue === 'boolean') { | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.checked = true; | |||
inputEl.checked = newValue; | |||
} | |||
}; | |||
@@ -324,9 +383,6 @@ const getInputFileFieldValue = ( | |||
options = {} as GetInputFileFieldValueOptions, | |||
) => { | |||
const { files } = inputEl; | |||
if ((files as unknown) === null) { | |||
return null; | |||
} | |||
if (options.getFileObjects) { | |||
return files; | |||
} | |||
@@ -395,17 +451,17 @@ const getInputNumericFieldValue = ( | |||
* @param inputEl - The element. | |||
* @param value - Value of the input element. | |||
* @param nthOfName - What order is this field in with respect to fields of the same name? | |||
* @param totalOfName - How many fields with the same name are in the form? | |||
* @param elementsWithSameName - How many fields with the same name are in the form? | |||
*/ | |||
const setInputNumericFieldValue = ( | |||
inputEl: HTMLInputNumericElement, | |||
value: unknown, | |||
nthOfName: number, | |||
totalOfName: number, | |||
elementsWithSameName: HTMLInputNumericElement[], | |||
) => { | |||
const valueArray = Array.isArray(value) ? value : [value]; | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.valueAsNumber = Number(valueArray[totalOfName > 1 ? nthOfName : 0]); | |||
inputEl.valueAsNumber = Number(valueArray[elementsWithSameName.length > 1 ? nthOfName : 0]); | |||
}; | |||
/** | |||
@@ -472,7 +528,11 @@ const getInputDateLikeFieldValue = ( | |||
options = {} as GetInputDateFieldValueOptions, | |||
) => { | |||
if (options.forceDateValues) { | |||
return inputEl.valueAsDate; | |||
return ( | |||
// somehow datetime-local does not return us the current `valueAsDate` when the string | |||
// representation in `value` is incomplete. | |||
inputEl.type === INPUT_TYPE_DATETIME_LOCAL ? new Date(inputEl.value) : inputEl.valueAsDate | |||
); | |||
} | |||
return inputEl.value; | |||
}; | |||
@@ -492,20 +552,22 @@ const DATE_FORMAT_ISO_MONTH = 'yyyy-MM' as const; | |||
* @param inputEl - The element. | |||
* @param value - Value of the input element. | |||
* @param nthOfName - What order is this field in with respect to fields of the same name? | |||
* @param totalOfName - How many fields with the same name are in the form? | |||
* @param elementsOfSameName - How many fields with the same name are in the form? | |||
*/ | |||
const setInputDateLikeFieldValue = ( | |||
inputEl: HTMLInputDateLikeElement, | |||
value: unknown, | |||
nthOfName: number, | |||
totalOfName: number, | |||
elementsOfSameName: HTMLInputDateLikeElement[], | |||
) => { | |||
const valueArray = Array.isArray(value) ? value : [value]; | |||
const hasMultipleElementsOfSameName = elementsOfSameName.length > 1; | |||
const elementIndex = hasMultipleElementsOfSameName ? nthOfName : 0; | |||
if (inputEl.type.toLowerCase() === INPUT_TYPE_DATE) { | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.value = new Date( | |||
valueArray[totalOfName > 1 ? nthOfName : 0] as ConstructorParameters<typeof Date>[0], | |||
valueArray[elementIndex] as ConstructorParameters<typeof Date>[0], | |||
) | |||
.toISOString() | |||
.slice(0, DATE_FORMAT_ISO_DATE.length); | |||
@@ -515,7 +577,7 @@ const setInputDateLikeFieldValue = ( | |||
if (inputEl.type.toLowerCase() === INPUT_TYPE_DATETIME_LOCAL) { | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.value = new Date( | |||
valueArray[totalOfName > 1 ? nthOfName : 0] as ConstructorParameters<typeof Date>[0], | |||
valueArray[elementIndex] as ConstructorParameters<typeof Date>[0], | |||
) | |||
.toISOString() | |||
.slice(0, -1); // remove extra 'Z' suffix | |||
@@ -524,7 +586,7 @@ const setInputDateLikeFieldValue = ( | |||
if (inputEl.type.toLowerCase() === INPUT_TYPE_MONTH) { | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.value = new Date( | |||
valueArray[totalOfName > 1 ? nthOfName : 0] as ConstructorParameters<typeof Date>[0], | |||
valueArray[elementIndex] as ConstructorParameters<typeof Date>[0], | |||
) | |||
.toISOString() | |||
.slice(0, DATE_FORMAT_ISO_MONTH.length); // remove extra 'Z' suffix | |||
@@ -580,6 +642,11 @@ const INPUT_TYPE_HIDDEN = 'hidden' as const; | |||
*/ | |||
const INPUT_TYPE_COLOR = 'color' as const; | |||
/** | |||
* Value of the `type` attribute for `<input>` elements considered as time pickers. | |||
*/ | |||
const INPUT_TYPE_TIME = 'time' as const; | |||
/** | |||
* Gets the value of an `<input>` element. | |||
* @param inputEl - The element. | |||
@@ -612,12 +679,13 @@ const getInputFieldValue = ( | |||
case INPUT_TYPE_PASSWORD: | |||
case INPUT_TYPE_HIDDEN: | |||
case INPUT_TYPE_COLOR: | |||
case INPUT_TYPE_TIME: | |||
default: | |||
break; | |||
} | |||
// force return `null` for custom elements supporting setting values. | |||
return inputEl.value ?? null; | |||
// don't force returning `null` for custom elements supporting setting values. | |||
return inputEl.value; | |||
}; | |||
/** | |||
@@ -625,7 +693,7 @@ const getInputFieldValue = ( | |||
* @param inputEl - The element. | |||
* @param value - Value of the input element. | |||
* @param nthOfName - What order is this field in with respect to fields of the same name? | |||
* @param totalOfName - How many fields with the same name are in the form? | |||
* @param elementsWithSameName - How many fields with the same name are in the form? | |||
* @note This function is a noop for `<input type="file">` because by design, file inputs are not | |||
* assignable programmatically. | |||
*/ | |||
@@ -633,7 +701,7 @@ const setInputFieldValue = ( | |||
inputEl: HTMLInputElement, | |||
value: unknown, | |||
nthOfName: number, | |||
totalOfName: number, | |||
elementsWithSameName: HTMLInputElement[], | |||
) => { | |||
switch (inputEl.type.toLowerCase()) { | |||
case INPUT_TYPE_CHECKBOX: | |||
@@ -651,7 +719,7 @@ const setInputFieldValue = ( | |||
inputEl as HTMLInputNumericElement, | |||
value, | |||
nthOfName, | |||
totalOfName, | |||
elementsWithSameName as HTMLInputNumericElement[], | |||
); | |||
return; | |||
case INPUT_TYPE_DATE: | |||
@@ -661,7 +729,7 @@ const setInputFieldValue = ( | |||
inputEl as HTMLInputDateLikeElement, | |||
value, | |||
nthOfName, | |||
totalOfName, | |||
elementsWithSameName as HTMLInputDateLikeElement[], | |||
); | |||
return; | |||
case INPUT_TYPE_TEXT: | |||
@@ -672,11 +740,12 @@ const setInputFieldValue = ( | |||
case INPUT_TYPE_PASSWORD: | |||
case INPUT_TYPE_HIDDEN: | |||
case INPUT_TYPE_COLOR: | |||
case INPUT_TYPE_TIME: | |||
default: | |||
break; | |||
} | |||
if (Array.isArray(value) && totalOfName > 1) { | |||
if (Array.isArray(value) && elementsWithSameName.length > 1) { | |||
// eslint-disable-next-line no-param-reassign | |||
inputEl.value = value[nthOfName]; | |||
return; | |||
@@ -700,23 +769,24 @@ type HTMLElementWithName | |||
= (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | HTMLButtonElement); | |||
/** | |||
* Gets the value of a field element. | |||
* Gets the value of an element regardless if it's a field element or not. | |||
* @param el - The field element. | |||
* @param options - The options. | |||
* @returns Value of the field element. | |||
* @returns Value of the element. | |||
*/ | |||
export const getFieldValue = (el: HTMLElement, options = {} as GetFieldValueOptions) => { | |||
export const getValue = (el: HTMLElement, options = {} as GetFieldValueOptions) => { | |||
switch (el.tagName) { | |||
case TAG_NAME_TEXTAREA: | |||
return getTextAreaFieldValue(el as HTMLTextAreaElement, options); | |||
case TAG_NAME_SELECT: | |||
return getSelectFieldValue(el as HTMLSelectElement); | |||
case TAG_NAME_INPUT: | |||
return getInputFieldValue(el as HTMLInputElement, options); | |||
default: | |||
break; | |||
} | |||
return getInputFieldValue(el as HTMLInputElement, options); | |||
return 'value' in el ? el.value : null; | |||
}; | |||
/** | |||
@@ -724,27 +794,42 @@ export const getFieldValue = (el: HTMLElement, options = {} as GetFieldValueOpti | |||
* @param el - The field element. | |||
* @param value - Value of the field element. | |||
* @param nthOfName - What order is this field in with respect to fields of the same name? | |||
* @param totalOfName - How many fields with the same name are in the form? | |||
* @param elementsWithSameName - How many fields with the same name are in the form? | |||
*/ | |||
const setFieldValue = ( | |||
el: HTMLElement, | |||
value: unknown, | |||
nthOfName: number, | |||
totalOfName: number, | |||
elementsWithSameName: HTMLElement[], | |||
) => { | |||
switch (el.tagName) { | |||
case TAG_NAME_TEXTAREA: | |||
setTextAreaFieldValue(el as HTMLTextAreaElement, value, nthOfName, totalOfName); | |||
setTextAreaFieldValue( | |||
el as HTMLTextAreaElement, | |||
value, | |||
nthOfName, | |||
elementsWithSameName as HTMLTextAreaElement[], | |||
); | |||
return; | |||
case TAG_NAME_SELECT: | |||
setSelectFieldValue(el as HTMLSelectElement, value); | |||
setSelectFieldValue( | |||
el as HTMLSelectElement, | |||
value, | |||
nthOfName, | |||
elementsWithSameName as HTMLSelectElement[], | |||
); | |||
return; | |||
case TAG_NAME_INPUT: | |||
default: | |||
break; | |||
} | |||
setInputFieldValue(el as HTMLInputElement, value, nthOfName, totalOfName); | |||
setInputFieldValue( | |||
el as HTMLInputElement, | |||
value, | |||
nthOfName, | |||
elementsWithSameName as HTMLInputElement[], | |||
); | |||
}; | |||
/** | |||
@@ -758,17 +843,17 @@ const ATTRIBUTE_NAME = 'name' as const; | |||
const ATTRIBUTE_DISABLED = 'disabled' as const; | |||
/** | |||
* Determines if an element is a named and enabled form field. | |||
* Determines if an element's value is included when its form is submitted. | |||
* @param el - The element. | |||
* @returns Value determining if the element is a named and enabled form field. | |||
* @returns Value determining if the element's value is included when its form is submitted. | |||
*/ | |||
export const isNamedEnabledFormFieldElement = (el: HTMLElement) => { | |||
export const isElementValueIncludedInFormSubmit = (el: HTMLElement) => { | |||
const namedEl = el as unknown as Record<string, unknown>; | |||
return ( | |||
typeof namedEl[ATTRIBUTE_NAME] === 'string' | |||
&& namedEl[ATTRIBUTE_NAME].length > 0 | |||
&& !(ATTRIBUTE_DISABLED in namedEl && Boolean(namedEl[ATTRIBUTE_DISABLED])) | |||
&& isFormFieldElement(namedEl as unknown as HTMLElement) | |||
&& isFieldElement(namedEl as unknown as HTMLElement) | |||
); | |||
}; | |||
@@ -829,7 +914,7 @@ const filterFieldElements = (form: HTMLFormElement) => { | |||
!Number.isNaN(Number(k)) | |||
// Only the enabled/read-only elements can be enumerated. | |||
&& isNamedEnabledFormFieldElement(el) | |||
&& isElementValueIncludedInFormSubmit(el) | |||
)) as [string, HTMLElementWithName][]; | |||
}; | |||
@@ -845,7 +930,7 @@ export const getFormValues = (form: HTMLFormElement, options = {} as GetFormValu | |||
const fieldElements = filterFieldElements(form); | |||
const fieldValues = fieldElements.reduce( | |||
(theFormValues, [, el]) => { | |||
const fieldValue = getFieldValue(el, options); | |||
const fieldValue = getValue(el, options); | |||
if (fieldValue === null) { | |||
return theFormValues; | |||
} | |||
@@ -853,24 +938,35 @@ export const getFormValues = (form: HTMLFormElement, options = {} as GetFormValu | |||
const { name: fieldName } = el; | |||
const { [fieldName]: oldFormValue = null } = theFormValues; | |||
if (oldFormValue === null) { | |||
if (oldFormValue !== null && !Array.isArray(oldFormValue)) { | |||
return { | |||
...theFormValues, | |||
[fieldName]: fieldValue, | |||
[fieldName]: [oldFormValue, fieldValue], | |||
}; | |||
} | |||
if (!Array.isArray(oldFormValue)) { | |||
if (Array.isArray(oldFormValue)) { | |||
if (Array.isArray(fieldValue)) { | |||
return { | |||
...theFormValues, | |||
[fieldName]: [...oldFormValue, ...fieldValue], | |||
}; | |||
} | |||
return { | |||
...theFormValues, | |||
[fieldName]: [oldFormValue, fieldValue], | |||
[fieldName]: [...oldFormValue, fieldValue], | |||
}; | |||
} | |||
return { | |||
...theFormValues, | |||
[fieldName]: [...oldFormValue, fieldValue], | |||
[fieldName]: fieldValue, | |||
}; | |||
// return { | |||
// ...theFormValues, | |||
// [fieldName]: [...oldFormValue, fieldValue], | |||
// }; | |||
}, | |||
{} as Record<string, unknown>, | |||
); | |||
@@ -928,36 +1024,46 @@ export const setFormValues = ( | |||
const fieldElements = filterFieldElements(form); | |||
const objectValues = normalizeValues(values); | |||
const count = fieldElements | |||
const elementsWithSameName = fieldElements | |||
.filter(([, el]) => el.name in objectValues) | |||
.reduce( | |||
(currentCount, [, el]) => { | |||
if (el.tagName === TAG_NAME_INPUT && el.type === INPUT_TYPE_RADIO) { | |||
return { | |||
...currentCount, | |||
[el.name]: 1, | |||
[el.name]: [el], | |||
}; | |||
} | |||
return { | |||
...currentCount, | |||
[el.name]: ( | |||
typeof currentCount[el.name] === 'number' | |||
? currentCount[el.name] + 1 | |||
: 1 | |||
Array.isArray(currentCount[el.name]) | |||
? [...currentCount[el.name], el] | |||
: [el] | |||
), | |||
}; | |||
}, | |||
{} as Record<string, number>, | |||
{} as Record<string, HTMLElement[]>, | |||
); | |||
const counter = {} as Record<string, number>; | |||
const nthElementOfName = {} as Record<string, number>; | |||
fieldElements | |||
.filter(([, el]) => el.name in objectValues) | |||
.forEach(([, el]) => { | |||
counter[el.name] = typeof counter[el.name] === 'number' ? counter[el.name] + 1 : 0; | |||
setFieldValue(el, objectValues[el.name], counter[el.name], count[el.name]); | |||
nthElementOfName[el.name] = ( | |||
typeof nthElementOfName[el.name] === 'number' | |||
? nthElementOfName[el.name] + 1 | |||
: 0 | |||
); | |||
setFieldValue( | |||
el, | |||
objectValues[el.name], | |||
nthElementOfName[el.name], | |||
elementsWithSameName[el.name], | |||
); | |||
}); | |||
}; | |||