@@ -1,6 +1,6 @@ | |||
{ | |||
"fixturesFolder": "test/fixtures", | |||
"integrationFolder": "test/integrations", | |||
"integrationFolder": "test/integration", | |||
"pluginsFile": "test/plugins/index.ts", | |||
"supportFile": "test/support/index.ts" | |||
} |
@@ -1,3 +1,3 @@ | |||
module.exports = { | |||
testEnvironment: 'node', | |||
testEnvironment: 'jsdom', | |||
}; |
@@ -13,9 +13,9 @@ | |||
"scripts": { | |||
"start": "tsdx watch", | |||
"build": "tsdx build", | |||
"test": "tsdx test", | |||
"test:jsdom": "tsdx test", | |||
"test:dom": "cypress open", | |||
"lint": "tsdx lint", | |||
"e2e": "cypress open", | |||
"prepare": "tsdx build", | |||
"size": "size-limit", | |||
"analyze": "size-limit --why" | |||
@@ -49,6 +49,7 @@ | |||
"@size-limit/preset-small-lib": "^4.10.2", | |||
"@types/jsdom": "^16.2.10", | |||
"cypress": "^7.2.0", | |||
"cypress-jest-adapter": "^0.1.1", | |||
"husky": "^6.0.0", | |||
"jsdom": "^16.5.3", | |||
"size-limit": "^4.10.2", | |||
@@ -1,90 +0,0 @@ | |||
import * as fixtures from '../test/utils' | |||
import getFormValues from '.' | |||
import {DOMWindow, JSDOM} from 'jsdom'; | |||
describe('blank template', () => { | |||
let window: DOMWindow | |||
beforeEach(async () => { | |||
window = new JSDOM(await fixtures.loadTemplate('blank')).window | |||
}) | |||
it('should have blank form value', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const values = getFormValues(form) | |||
expect(values).toEqual({}) | |||
}) | |||
}) | |||
describe('single input template', () => { | |||
let window: DOMWindow | |||
beforeEach(async () => { | |||
window = new JSDOM(await fixtures.loadTemplate('single-input')).window | |||
}) | |||
it('should have a single form value', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const values = getFormValues(form) | |||
expect(values).toEqual({ hello: 'Hi' }) | |||
}) | |||
}) | |||
describe('single disabled input template', () => { | |||
let window: DOMWindow | |||
beforeEach(async () => { | |||
window = new JSDOM(await fixtures.loadTemplate('single-disabled-input')).window | |||
}) | |||
it('should have blank form value', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const values = getFormValues(form) | |||
expect(values).toEqual({}) | |||
}) | |||
}) | |||
describe('single readonly input template', () => { | |||
let window: DOMWindow | |||
beforeEach(async () => { | |||
window = new JSDOM(await fixtures.loadTemplate('single-readonly-input')).window | |||
}) | |||
it('should have a single form value', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const values = getFormValues(form) | |||
expect(values).toEqual({ hello: 'Hi' }) | |||
}) | |||
}) | |||
describe('single input with double button submitters template', () => { | |||
let window: DOMWindow | |||
beforeEach(async () => { | |||
window = new JSDOM(await fixtures.loadTemplate('single-input-with-double-button-submitters')).window | |||
}) | |||
it('should have a single form value', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const values = getFormValues(form) | |||
expect(values).toEqual({ hello: 'Hi' }) | |||
}) | |||
it('should include the submitter\'s value when provided', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const [submitter] = Array.from(window.document.getElementsByTagName('button')) | |||
const values = getFormValues(form, submitter) | |||
expect(values).toEqual({ hello: 'Hi', [submitter.name]: submitter.value }) | |||
}) | |||
}) | |||
describe('single input with double input submitters template', () => { | |||
let window: DOMWindow | |||
beforeEach(async () => { | |||
window = new JSDOM(await fixtures.loadTemplate('single-input-with-double-input-submitters')).window | |||
}) | |||
it('should have a single form value', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const values = getFormValues(form) | |||
expect(values).toEqual({ hello: 'Hi' }) | |||
}) | |||
it('should include the submitter\'s value when provided', () => { | |||
const [form] = Array.from(window.document.getElementsByTagName('form')) | |||
const [,submitter] = Array.from(window.document.getElementsByTagName('input')).filter(i => i.type === 'submit') | |||
const values = getFormValues(form, submitter) | |||
expect(values).toEqual({ hello: 'Hi', [submitter.name]: submitter.value }) | |||
}) | |||
}) |
@@ -1,26 +1,14 @@ | |||
const RadioNodeList = global.RadioNodeList || class RadioNodeList { | |||
name: string = '' | |||
disabled: boolean = false | |||
} | |||
type HTMLFieldElement | |||
= HTMLInputElement | |||
| HTMLButtonElement | |||
| HTMLSelectElement | |||
| HTMLTextAreaElement | |||
type FieldNode | |||
= typeof RadioNodeList | |||
| HTMLFieldElement | |||
type HTMLSubmitterElement | |||
= HTMLButtonElement | |||
| HTMLInputElement | |||
const isFormFieldElement = (el: FieldNode) => { | |||
if ((el as unknown) instanceof RadioNodeList) { | |||
return true | |||
} | |||
const isFormFieldElement = (el: HTMLFieldElement) => { | |||
const htmlEl = el as HTMLElement | |||
const tagName = htmlEl.tagName | |||
if (['SELECT', 'TEXTAREA'].includes(tagName)) { | |||
@@ -41,71 +29,11 @@ const isFormFieldElement = (el: FieldNode) => { | |||
return Boolean(inputEl.name) | |||
} | |||
const isValidFieldNode = (submitter?: HTMLSubmitterElement) => (fieldNode: Node) => { | |||
const fieldEl = fieldNode as HTMLElement | |||
const fieldElTagName = fieldEl.tagName | |||
if (fieldElTagName === 'BUTTON' && Boolean(submitter as HTMLSubmitterElement)) { | |||
const buttonEl = fieldEl as HTMLButtonElement | |||
if (buttonEl.type === 'reset' || buttonEl.type === 'button') { | |||
return false | |||
} | |||
return ( | |||
buttonEl.name === submitter!.name | |||
&& buttonEl.value === submitter!.value | |||
) | |||
} | |||
if (fieldElTagName === 'INPUT') { | |||
const inputEl = fieldEl as HTMLInputElement | |||
if (inputEl.type === 'radio') { | |||
return inputEl.checked | |||
} | |||
if (inputEl.type === 'submit' && Boolean(submitter as HTMLSubmitterElement)) { | |||
return ( | |||
inputEl.name === submitter!.name | |||
&& inputEl.value === submitter!.value | |||
) | |||
} | |||
if (inputEl.type === 'reset' || inputEl.type === 'button') { | |||
return false | |||
} | |||
} | |||
return true | |||
} | |||
const getRadioNodeListResolvedValue = (radioNodeList: RadioNodeList, submitter?: HTMLSubmitterElement) => { | |||
const isValid = isValidFieldNode(submitter) | |||
const validFieldElements: Node[] = Array.from(radioNodeList).filter(isValid) | |||
if (validFieldElements.length > 1) { | |||
return validFieldElements.map((fieldNode: Node) => (fieldNode as HTMLFieldElement).value) | |||
} | |||
if (validFieldElements.length > 0) { | |||
const [validFieldElement] = (validFieldElements as HTMLFieldElement[]) | |||
if (validFieldElement) { | |||
return validFieldElement.value | |||
} | |||
} | |||
return null | |||
} | |||
/** | |||
* Gets the value of a field element. | |||
* @param el - The field element node. | |||
* @param submitter - The element which triggered the enclosing form's submit event, if said form is submitted. | |||
*/ | |||
const getFieldValue = (el: FieldNode, submitter?: HTMLSubmitterElement) => { | |||
if ((el as unknown) instanceof RadioNodeList) { | |||
return getRadioNodeListResolvedValue(el as unknown as RadioNodeList, submitter) | |||
} | |||
const getFieldValue = (el: HTMLFieldElement) => { | |||
const fieldEl = el as HTMLFieldElement | |||
const tagName = fieldEl.tagName | |||
const type = fieldEl.type | |||
@@ -149,7 +77,7 @@ const getFieldValue = (el: FieldNode, submitter?: HTMLSubmitterElement) => { | |||
* Returns only named form field elements. | |||
* @param el - The element | |||
*/ | |||
const isValidFormField = (el: FieldNode) => { | |||
const isValidFormField = (el: HTMLFieldElement) => { | |||
return ( | |||
'name' in el | |||
&& typeof el['name'] === 'string' | |||
@@ -166,8 +94,8 @@ const getFormValues = (form: HTMLFormElement, submitter?: HTMLSubmitterElement) | |||
if (!form) { | |||
throw new TypeError('Invalid form element.') | |||
} | |||
const formElements = form.elements as unknown as Record<string | number, FieldNode> | |||
const allFormFieldElements = Object.entries<FieldNode>(formElements) | |||
const formElements = form.elements as unknown as Record<string | number, HTMLFieldElement> | |||
const allFormFieldElements = Object.entries<HTMLFieldElement>(formElements) | |||
const formFieldElements = allFormFieldElements.filter(([k, el]) => { | |||
return ( | |||
// get only indexed forms | |||
@@ -177,7 +105,7 @@ const getFormValues = (form: HTMLFormElement, submitter?: HTMLSubmitterElement) | |||
}) | |||
const fieldValues = formFieldElements.reduce( | |||
(theFormValues, [,el]) => { | |||
const fieldValue = getFieldValue(el, submitter) | |||
const fieldValue = getFieldValue(el) | |||
if (fieldValue === null) { | |||
return theFormValues | |||
} | |||
@@ -5,6 +5,8 @@ | |||
<title>Title</title> | |||
</head> | |||
<body> | |||
<form></form> | |||
<form> | |||
<button type="submit">Submit</button> | |||
</form> | |||
</body> | |||
</html> |
@@ -0,0 +1,23 @@ | |||
/// <reference types="cypress" /> | |||
/// <reference types="cypress-jest-adapter" /> | |||
import getFormValues from '../../src' | |||
import * as utils from '../utils' | |||
describe('blank template', () => { | |||
beforeEach(utils.setup('blank')) | |||
it('should have blank form value', () => { | |||
utils.test( | |||
(cy: any) => { | |||
cy.get('[type="submit"]').click() | |||
}, | |||
(form: HTMLFormElement, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form)).toString(); | |||
const after = utils.makeSearchParams(search).toString(); | |||
expect(before).toEqual(after); | |||
}, | |||
{} | |||
); | |||
}); | |||
}) |
@@ -0,0 +1,42 @@ | |||
/// <reference types="cypress" /> | |||
/// <reference types="cypress-jest-adapter" /> | |||
import getFormValues from '../../src' | |||
import * as utils from '../utils'; | |||
describe('blank template', () => { | |||
beforeEach(utils.setup('everything')) | |||
it('should have blank form value', () => { | |||
let submitter: HTMLButtonElement | |||
utils.test( | |||
(cy) => { | |||
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"]').last().type('Juana') | |||
cy.get('button.dependents').click() | |||
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Jane') | |||
cy.get('button.dependents').click() | |||
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Josh') | |||
cy.get('[name="notes"]').type('Test content\n\nNew line\n\nAnother line') | |||
cy.get('[name="submit"][value="Hi"]').then((submitterEl: any) => { | |||
[submitter] = Array.from(submitterEl) as HTMLButtonElement[]; | |||
submitterEl.trigger('click'); | |||
}) | |||
}, | |||
(form: HTMLFormElement, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).toEqual(after); | |||
}, | |||
{} | |||
); | |||
}); | |||
}) |
@@ -0,0 +1,114 @@ | |||
/// <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"]').click() | |||
}, | |||
(form: HTMLFormElement, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form)).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"]').click() | |||
}, | |||
(form: HTMLFormElement, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form)).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"]').click() | |||
}, | |||
(form: HTMLFormElement, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form)).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', () => { | |||
let submitter: HTMLButtonElement | |||
utils.test( | |||
(cy: any) => { | |||
cy.get('[name="action"][value="Foo"]') | |||
.then((result: any) => { | |||
[submitter] = Array.from(result) | |||
}) | |||
.click() | |||
}, | |||
(form: HTMLFormElement, 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', () => { | |||
let submitter: HTMLInputElement | |||
utils.test( | |||
(cy: any) => { | |||
cy.get('[name="action"][value="Bar"]') | |||
.then((result: any) => { | |||
[submitter] = Array.from(result) | |||
}) | |||
.click() | |||
}, | |||
(form: HTMLFormElement, search: any) => { | |||
const before = utils.makeSearchParams(getFormValues(form, submitter)).toString(); | |||
const after = utils.makeSearchParams(search).toString(); | |||
expect(before).toEqual(after); | |||
}, | |||
{ | |||
hello: 'Hi', | |||
action: 'Bar', | |||
} | |||
); | |||
}); | |||
}) |
@@ -1,29 +0,0 @@ | |||
/// <reference types="cypress" /> | |||
import getFormValues from '../../src' | |||
import {makeSearchParams} from '../utils/search'; | |||
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 form; | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
.submit() | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).to.equal(after); | |||
}) | |||
}); | |||
}) |
@@ -1,104 +0,0 @@ | |||
/// <reference types="cypress" /> | |||
import getFormValues from '../../src' | |||
import {makeSearchParams} from '../utils/search'; | |||
describe('default template', () => { | |||
beforeEach(() => { | |||
cy.intercept({ url: '/' }, { fixture: 'templates/everything.html' }); | |||
cy.intercept({ url: '/?*' }, { fixture: 'templates/everything.html' }).as('submitted'); | |||
}) | |||
it('should have a single form value', () => { | |||
let form; | |||
let submitter; | |||
cy | |||
.visit('/') | |||
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"]') | |||
.last() | |||
.type('Juana') | |||
cy | |||
.get('button.dependents') | |||
.click() | |||
cy | |||
.get('.additional-dependent [name="dependent"][type="text"]') | |||
.last() | |||
.type('Jane') | |||
cy | |||
.get('button.dependents') | |||
.click() | |||
cy | |||
.get('.additional-dependent [name="dependent"][type="text"]') | |||
.last() | |||
.type('Josh') | |||
cy | |||
.get('[name="notes"]') | |||
.type('Test content\n\nNew line\n\nAnother line') | |||
cy | |||
.get('form') | |||
.then((theForm) => { | |||
[form] = Array.from(theForm) | |||
}) | |||
cy | |||
.get('[name="submit"][value="Hi"]') | |||
.then((submitterEl) => { | |||
[submitter] = Array.from(submitterEl) as HTMLButtonElement[]; | |||
submitterEl.trigger('click'); | |||
}) | |||
cy | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form, submitter)); | |||
const after = new URLSearchParams(search) | |||
expect(before.toString()).to.equal(after.toString()); | |||
}) | |||
}) | |||
}); |
@@ -1,162 +0,0 @@ | |||
/// <reference types="cypress" /> | |||
import getFormValues from '../../src' | |||
import {makeSearchParams} from '../utils/search'; | |||
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 form | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
.get('[type="submit"]') | |||
.click() | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).to.equal(after); | |||
}) | |||
}) | |||
}); | |||
describe('single readonly 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 form | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
.get('[type="submit"]') | |||
.click() | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).to.equal(after); | |||
}) | |||
}) | |||
}); | |||
describe('single disabled 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 form | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
.get('[type="submit"]') | |||
.click() | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).to.equal(after); | |||
}) | |||
}) | |||
}); | |||
describe('single input with double button submitters template', () => { | |||
beforeEach(() => { | |||
cy | |||
.intercept( | |||
{ url: '/' }, | |||
{ fixture: 'templates/single-input-with-double-button-submitters.html' } | |||
); | |||
cy | |||
.intercept( | |||
{ url: '/?*' }, | |||
{ fixture: 'templates/single-input-with-double-button-submitters.html' } | |||
) | |||
.as('submitted'); | |||
}) | |||
it('should have a single form value', () => { | |||
let submitter; | |||
let form; | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
.get('[name="action"][value="Bar"]') | |||
.then((submitterEl) => { | |||
[submitter] = Array.from(submitterEl) | |||
}) | |||
.click() | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form, submitter as HTMLInputElement)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).to.equal(after); | |||
}) | |||
}) | |||
}); | |||
describe('single input with double input submitters 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 submitter; | |||
let form; | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
.get('[name="action"][value="Foo"]') | |||
.then((submitterEl) => { | |||
[submitter] = Array.from(submitterEl) | |||
}) | |||
.click() | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
const before = makeSearchParams(getFormValues(form, submitter as HTMLInputElement)).toString(); | |||
const after = new URLSearchParams(search).toString(); | |||
expect(before).to.equal(after); | |||
}) | |||
}) | |||
}); |
@@ -15,6 +15,7 @@ | |||
// Import commands.ts using ES2015 syntax: | |||
import './commands' | |||
import 'cypress-jest-adapter' | |||
// Alternatively you can use CommonJS syntax: | |||
// require('./commands') |
@@ -1,8 +1,70 @@ | |||
/// <reference types="cypress" /> | |||
/// <reference types="cypress-jest-adapter" /> | |||
import fs from 'fs' | |||
import path from 'path' | |||
export const loadTemplate = async (templateName: string): Promise<string> => { | |||
const templatePath = path.join('test', 'fixtures', 'templates', `${templateName}.html`) | |||
const templateRaw = await fs.promises.readFile(templatePath) | |||
return templateRaw.toString('utf-8') | |||
type TestFn = (form: HTMLFormElement, after: Record<string, string> | string) => unknown | |||
export const setup = (template: string) => { | |||
if (typeof cy !== 'undefined') { | |||
return () => { | |||
cy.intercept({ url: '/' }, { fixture: `templates/${template}.html` }); | |||
cy.intercept({ url: '/?*' }, { fixture: `templates/${template}.html` }).as('submitted'); | |||
} | |||
} | |||
return async () => { | |||
const templatePath = path.join('test', 'fixtures', 'templates', `${template}.html`) | |||
const templateRaw = await fs.promises.readFile(templatePath) | |||
window.document.open() | |||
window.document.write(templateRaw.toString('utf-8')) | |||
window.document.close() | |||
} | |||
} | |||
export const test = (opFn: (wrapper: any) => unknown, testFn: TestFn, expectedValue: Record<string, string> | string) => { | |||
let form: HTMLFormElement | |||
if (typeof cy !== 'undefined') { | |||
cy | |||
.visit('/') | |||
.get('form') | |||
.then((formResult) => { | |||
[form] = Array.from(formResult); | |||
}) | |||
opFn(cy) | |||
cy | |||
.wait('@submitted') | |||
.location('search') | |||
.then(search => { | |||
testFn(form, search) | |||
}) | |||
} else { | |||
[form] = Array.from(window.document.getElementsByTagName('form')) | |||
testFn(form, expectedValue) | |||
} | |||
} | |||
export const makeSearchParams = (beforeValues: Record<string, unknown> | string) => { | |||
switch (typeof (beforeValues as unknown)) { | |||
case 'string': | |||
return new URLSearchParams(beforeValues as string) | |||
case 'object': | |||
return Object | |||
.entries(beforeValues) | |||
.reduce( | |||
(beforeSearchParams, [key, value]) => { | |||
const theValue = !Array.isArray(value) ? [value] : value | |||
theValue.forEach(v => { | |||
beforeSearchParams.append(key, v) | |||
}) | |||
return beforeSearchParams | |||
}, | |||
new URLSearchParams() | |||
) | |||
default: | |||
break | |||
} | |||
throw new TypeError('Invalid parameter.') | |||
} |
@@ -1,12 +0,0 @@ | |||
export const makeSearchParams = (beforeValues) => Object | |||
.entries(beforeValues) | |||
.reduce( | |||
(beforeSearchParams, [key, value]) => { | |||
const theValue = !Array.isArray(value) ? [value] : value | |||
theValue.forEach(v => { | |||
beforeSearchParams.append(key, v) | |||
}) | |||
return beforeSearchParams | |||
}, | |||
new URLSearchParams() | |||
) |
@@ -976,6 +976,15 @@ | |||
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" | |||
integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== | |||
"@jest/console@^24.9.0": | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" | |||
integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== | |||
dependencies: | |||
"@jest/source-map" "^24.9.0" | |||
chalk "^2.0.1" | |||
slash "^2.0.0" | |||
"@jest/console@^25.5.0": | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.5.0.tgz#770800799d510f37329c508a9edd0b7b447d9abb" | |||
@@ -1082,6 +1091,15 @@ | |||
optionalDependencies: | |||
node-notifier "^6.0.0" | |||
"@jest/source-map@^24.9.0": | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" | |||
integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== | |||
dependencies: | |||
callsites "^3.0.0" | |||
graceful-fs "^4.1.15" | |||
source-map "^0.6.0" | |||
"@jest/source-map@^25.5.0": | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.5.0.tgz#df5c20d6050aa292c2c6d3f0d2c7606af315bd1b" | |||
@@ -1091,6 +1109,15 @@ | |||
graceful-fs "^4.2.4" | |||
source-map "^0.6.0" | |||
"@jest/test-result@^24.9.0": | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" | |||
integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== | |||
dependencies: | |||
"@jest/console" "^24.9.0" | |||
"@jest/types" "^24.9.0" | |||
"@types/istanbul-lib-coverage" "^2.0.0" | |||
"@jest/test-result@^25.5.0": | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.5.0.tgz#139a043230cdeffe9ba2d8341b27f2efc77ce87c" | |||
@@ -1134,6 +1161,15 @@ | |||
source-map "^0.6.1" | |||
write-file-atomic "^3.0.0" | |||
"@jest/types@^24.9.0": | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" | |||
integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== | |||
dependencies: | |||
"@types/istanbul-lib-coverage" "^2.0.0" | |||
"@types/istanbul-reports" "^1.1.1" | |||
"@types/yargs" "^13.0.0" | |||
"@jest/types@^25.5.0": | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" | |||
@@ -1442,6 +1478,13 @@ | |||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" | |||
integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== | |||
"@types/yargs@^13.0.0": | |||
version "13.0.11" | |||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.11.tgz#def2f0c93e4bdf2c61d7e34899b17e34be28d3b1" | |||
integrity sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ== | |||
dependencies: | |||
"@types/yargs-parser" "*" | |||
"@types/yargs@^15.0.0": | |||
version "15.0.13" | |||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc" | |||
@@ -1755,7 +1798,7 @@ ansi-regex@^3.0.0: | |||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" | |||
integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= | |||
ansi-regex@^4.1.0: | |||
ansi-regex@^4.0.0, ansi-regex@^4.1.0: | |||
version "4.1.0" | |||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" | |||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== | |||
@@ -2506,7 +2549,7 @@ chalk@^1.0.0, chalk@^1.1.3: | |||
strip-ansi "^3.0.0" | |||
supports-color "^2.0.0" | |||
chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: | |||
chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: | |||
version "2.4.2" | |||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" | |||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== | |||
@@ -3139,6 +3182,16 @@ cyclist@^1.0.1: | |||
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" | |||
integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= | |||
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" | |||
integrity sha512-5dSB03utqDTBG5pi1LaAvYQD5uSMtSwurSzodpM+3XS/RdrjR/644oPnFUxPRvX4FVBaIY8avRs/f/GmIAiu8w== | |||
dependencies: | |||
expect "^24.5.0" | |||
jest-get-type "^24.3.0" | |||
jest-jquery-matchers "^2.1.0" | |||
jquery "^3.4.0" | |||
cypress@^7.2.0: | |||
version "7.2.0" | |||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-7.2.0.tgz#6a3364e18972f898fff1fb12c1ff747939e45ddc" | |||
@@ -3331,6 +3384,11 @@ detect-newline@^3.0.0: | |||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" | |||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== | |||
diff-sequences@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" | |||
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== | |||
diff-sequences@^25.2.6: | |||
version "25.2.6" | |||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" | |||
@@ -3940,6 +3998,18 @@ expand-brackets@^2.1.4: | |||
snapdragon "^0.8.1" | |||
to-regex "^3.0.1" | |||
expect@^24.5.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" | |||
integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== | |||
dependencies: | |||
"@jest/types" "^24.9.0" | |||
ansi-styles "^3.2.0" | |||
jest-get-type "^24.9.0" | |||
jest-matcher-utils "^24.9.0" | |||
jest-message-util "^24.9.0" | |||
jest-regex-util "^24.9.0" | |||
expect@^25.5.0: | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/expect/-/expect-25.5.0.tgz#f07f848712a2813bb59167da3fb828ca21f58bba" | |||
@@ -5209,6 +5279,16 @@ jest-config@^25.5.4: | |||
pretty-format "^25.5.0" | |||
realpath-native "^2.0.0" | |||
jest-diff@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" | |||
integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== | |||
dependencies: | |||
chalk "^2.0.1" | |||
diff-sequences "^24.9.0" | |||
jest-get-type "^24.9.0" | |||
pretty-format "^24.9.0" | |||
jest-diff@^25.2.1, jest-diff@^25.5.0: | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" | |||
@@ -5261,6 +5341,11 @@ jest-environment-node@^25.5.0: | |||
jest-util "^25.5.0" | |||
semver "^6.3.0" | |||
jest-get-type@^24.3.0, jest-get-type@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" | |||
integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== | |||
jest-get-type@^25.2.6: | |||
version "25.2.6" | |||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" | |||
@@ -5309,6 +5394,11 @@ jest-jasmine2@^25.5.4: | |||
pretty-format "^25.5.0" | |||
throat "^5.0.0" | |||
jest-jquery-matchers@^2.1.0: | |||
version "2.1.0" | |||
resolved "https://registry.yarnpkg.com/jest-jquery-matchers/-/jest-jquery-matchers-2.1.0.tgz#b56d19c460f6e57b17e6ddcce6f905d28b0ec532" | |||
integrity sha512-gbCptZOPFv4m7CJenRJcp7OTmQ432yakCDGup6gExrIwbNq8hBfxU+llXO2IXOpFuLmI0c8A+JdGMUNXe6LV9A== | |||
jest-leak-detector@^25.5.0: | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz#2291c6294b0ce404241bb56fe60e2d0c3e34f0bb" | |||
@@ -5317,6 +5407,16 @@ jest-leak-detector@^25.5.0: | |||
jest-get-type "^25.2.6" | |||
pretty-format "^25.5.0" | |||
jest-matcher-utils@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" | |||
integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== | |||
dependencies: | |||
chalk "^2.0.1" | |||
jest-diff "^24.9.0" | |||
jest-get-type "^24.9.0" | |||
pretty-format "^24.9.0" | |||
jest-matcher-utils@^25.5.0: | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867" | |||
@@ -5327,6 +5427,20 @@ jest-matcher-utils@^25.5.0: | |||
jest-get-type "^25.2.6" | |||
pretty-format "^25.5.0" | |||
jest-message-util@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" | |||
integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== | |||
dependencies: | |||
"@babel/code-frame" "^7.0.0" | |||
"@jest/test-result" "^24.9.0" | |||
"@jest/types" "^24.9.0" | |||
"@types/stack-utils" "^1.0.1" | |||
chalk "^2.0.1" | |||
micromatch "^3.1.10" | |||
slash "^2.0.0" | |||
stack-utils "^1.0.1" | |||
jest-message-util@^25.5.0: | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" | |||
@@ -5353,6 +5467,11 @@ jest-pnp-resolver@^1.2.1: | |||
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" | |||
integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== | |||
jest-regex-util@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" | |||
integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== | |||
jest-regex-util@^25.2.1, jest-regex-util@^25.2.6: | |||
version "25.2.6" | |||
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" | |||
@@ -5545,6 +5664,11 @@ jpjs@^1.2.1: | |||
resolved "https://registry.yarnpkg.com/jpjs/-/jpjs-1.2.1.tgz#f343833de8838a5beba1f42d5a219be0114c44b7" | |||
integrity sha512-GxJWybWU4NV0RNKi6EIqk6IRPOTqd/h+U7sbtyuD7yUISUzV78LdHnq2xkevJsTlz/EImux4sWj+wfMiwKLkiw== | |||
jquery@^3.4.0: | |||
version "3.6.0" | |||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" | |||
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== | |||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: | |||
version "4.0.0" | |||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" | |||
@@ -7210,6 +7334,16 @@ pretty-bytes@^5.6.0: | |||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" | |||
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== | |||
pretty-format@^24.9.0: | |||
version "24.9.0" | |||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" | |||
integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== | |||
dependencies: | |||
"@jest/types" "^24.9.0" | |||
ansi-regex "^4.0.0" | |||
ansi-styles "^3.2.0" | |||
react-is "^16.8.4" | |||
pretty-format@^25.2.1, pretty-format@^25.5.0: | |||
version "25.5.0" | |||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" | |||
@@ -7374,7 +7508,7 @@ randomfill@^1.0.3: | |||
randombytes "^2.0.5" | |||
safe-buffer "^5.1.0" | |||
react-is@^16.12.0, react-is@^16.8.1: | |||
react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4: | |||
version "16.13.1" | |||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" | |||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== | |||
@@ -8043,6 +8177,11 @@ size-limit@^4.10.2: | |||
ora "^5.4.0" | |||
read-pkg-up "^7.0.1" | |||
slash@^2.0.0: | |||
version "2.0.0" | |||
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" | |||
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== | |||
slash@^3.0.0: | |||
version "3.0.0" | |||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" | |||