Skip to content

Commit

Permalink
test: update tests for input-text, fix useModify
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenesius committed Aug 9, 2023
1 parent 80a0ff1 commit 221990e
Show file tree
Hide file tree
Showing 8 changed files with 280 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/classes/Form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,9 @@ export default class Form extends EventEmitter implements FormDependence {
* */
validate(): boolean {
const result = this.dependencies.reduce((acc, dep) => {
if (typeof dep.validate === "function") acc = acc && !!dep.validate();
const depValidationResult = (typeof dep.validate === "function") ? dep.validate() : true;
console.log("Dep validation result:", depValidationResult)
acc = acc && !!depValidationResult;
return acc;
}, true);

Expand Down
3 changes: 1 addition & 2 deletions src/local-hooks/use-modify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export default function useModify(callbackModifyProps: () => ModifyParam) {

return function execute(v: unknown): string {
const arr = parse(callbackModifyProps()); // Getting array of handlers

arr.forEach(callback => v = callback(v));

try {
arr.forEach(callback => v = callback(v));
} catch (e) {
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type Value = Values | any;
* @description Callback использующийся для валидации поля для ввода.
* */
export type FormInputValidationCallback = (values: any) => boolean | string
export type ValidationError = string | false

export type ValidationRule = () => boolean | string;

Expand Down
4 changes: 3 additions & 1 deletion src/widgets/field-wrap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
</template>

<script setup lang="ts">
import {ValidationError} from "../types";
defineProps<{
label?: string,
errors?: string[]
errors?: ValidationError[]
}>()
</script>

Expand Down
31 changes: 24 additions & 7 deletions src/widgets/form-field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
:disabled = "input?.disabled"
:changed = "input?.changed"
:errors="input?.errors"
:errors="input?.errors || []"
/>
</template>
Expand All @@ -18,11 +18,13 @@ import {computed, onMounted, onUnmounted, reactive} from "vue";
import {getFieldType} from "../config/store";
import Form from "../classes/Form";
import {FormInputValidationCallback} from "../types";
import STORE from "../../plugin/config/store";
interface IProps {
name: string,
type?: string,
validation?: FormInputValidationCallback[]
validation?: FormInputValidationCallback[] | FormInputValidationCallback,
required?: boolean
}
const props = defineProps<IProps>()
const parentForm = Form.getParentForm();
Expand Down Expand Up @@ -67,15 +69,17 @@ function useFormInput(name: string) {
const InputDependency = {
name,
validation() {
validate() {
console.log("Input validation:", validation, input.value);
const result = validation.reduce((acc: (string | boolean)[], guard) => {
const guardResult = guard(input.value);
if (guardResult !== true) acc.push(guardResult);
return acc;
}, []);
input.errors = result;
return result.length
return result.length === 0
}
}
Expand All @@ -87,15 +91,28 @@ function useFormInput(name: string) {
parentForm.unsubscribe(InputDependency);
})
function setValidation(array: FormInputValidationCallback[]) {
validation = array;
function setValidation(array?: FormInputValidationCallback[] | FormInputValidationCallback) {
validation = typeof array === 'function' ? [array] : (array || []);
}
return input;
}
const input = useFormInput(props.name);
function mergeValidation() {
const arr:FormInputValidationCallback[] = [];
if (props.validation) {
if (typeof props.validation === 'function') arr.push(props.validation);
else arr.push(...props.validation)
}
if (props.required) arr.unshift((v: any) => !!v || STORE.requiredMessage)
return arr;
}
const input = useFormInput(props.name)
// @ts-ignore;
input?.setValidation(mergeValidation())
</script>
<style>
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/input-text/input-text.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@
<script setup lang="ts">
import {ref, watch} from "vue";
import useModify from "./../../local-hooks/use-modify";
import {StringModify} from "./../../types";
import {FormInputValidationCallback, StringModify, ValidationError} from "./../../types";
import onlyNumber from "./../../local-hooks/only-number";
import FieldWrap from "../field-wrap.vue";
const errors = [];
const props = defineProps<{
label?: string,
Expand All @@ -47,6 +46,7 @@ const props = defineProps<{
prefix?: string,
name?: string
numeric?: boolean,
errors: ValidationError[]
}>()
const isFocused = ref(false);
Expand Down
30 changes: 30 additions & 0 deletions tests/integrations/components/input-text/AppInputTextPretty.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template>
<div>
<input-field name="name" :pretty="pretty" :modify="modify"/>
</div>
</template>

<script setup lang="ts">
import InputField from "../../../../src/widgets/form-field.vue";
import Form from "../../../../src/classes/Form";
const form = new Form();
defineExpose({
form
})
const pretty = (x: unknown) => {
if (typeof x !== 'string') return '';
if (x.length) return `-${x}-`;
return ''
}
const modify = (x: string) => {
// @ts-ignore
return x.replaceAll(/[^a-zA-Z]/g, '');
}
</script>

<style scoped>
</style>
216 changes: 216 additions & 0 deletions tests/integrations/input-text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import Form from "../../src/classes/Form";
import {defineComponent} from "vue";
import {InputField} from "../../src//index";
import wait from "../wait";
import {FormInputValidationCallback} from "@/types";
import STORE from "../../src/config/store";
import AppInputTextPretty from "./components/input-text/AppInputTextPretty.vue"

describe("Input text", () => {
test("Default empty input-text", async () => {
Expand Down Expand Up @@ -176,4 +179,217 @@ describe("Input text", () => {
const inputs = app.findAll('input');
expect(inputs.map(a => a.element.value)).toEqual(["T", "T"])
})

test("Input-text with validation", async () => {
const test:FormInputValidationCallback[] = [
x => x !== 'Jack'
];
const component = defineComponent({
template: `<div>
<input-field name = "username" :validation = "${test}"/>
</div>`,
components: {InputField}
})
const app = mount(EmptyApp, {
slots: {
default: component
},
});
const form = (app.vm as any).form as Form;
app.vm.loadingResource = true
await app.vm.$nextTick()

expect(form.validate()).toBe(true);
form.setValues({
username: "Jack"
})

await app.vm.$nextTick()

expect(form.validate()).toBe(false);
form.setValues({
username: "Jack-1"
})
await app.vm.$nextTick()
expect(form.validate()).toBe(true)
})
test("Input should has error effect when one of validation was rejected", async () => {
const ERROR_TEXT = 'Jack is rejected'
const test:FormInputValidationCallback[] = [
x => x === 'Jack' ? true : 'Jack is rejected'
];
const component = defineComponent({
template: `<div><input-field name = "username" :validation = "${test}"/></div>`,
components: {InputField}
})
const app = mount(EmptyApp, {
slots: {
default: component
},
});
const form = (app.vm as any).form as Form;
app.vm.loadingResource = true
await app.vm.$nextTick()

const validateResult = form.validate()
expect(validateResult).toBe(false);
await app.vm.$nextTick();
await wait()
expect(app.find('.container-input-text_error').exists()).toBe(true);
expect(app.text()).toBe(ERROR_TEXT);

form.setValues({
username: "Jack"
})
form.validate()
await app.vm.$nextTick()
await wait()

expect(app.find('.container-input-text_error').exists()).toBe(false);
expect(app.text()).toBe("");
})
test("Input that required param should has required label and should be validation reject if empty", async () => {
const component = defineComponent({
template: `<div><input-field name = "username" required/></div>`,
components: {InputField}
})
const app = mount(EmptyApp, {
slots: { default: component },
});
const form = (app.vm as any).form as Form;
app.vm.loadingResource = true
await app.vm.$nextTick()

expect(form.validate()).toBe(false);

await wait();

expect(app.text()).toBe(STORE.requiredMessage);
form.setValues({
username: "PP"
})
expect(form.validate()).toBe(true);
await wait()
expect(app.text()).toBe("")
})
test("If label is provided it should be visible", async () => {
const component = defineComponent({
template: `<div><input-field name = "username" label = "Your name"/></div>`,
components: {InputField}
})
const app = mount(EmptyApp, {
slots: { default: component },
});
const form = (app.vm as any).form as Form;
app.vm.loadingResource = true
await app.vm.$nextTick()

expect(app.text()).toBe("Your name");
});
test("Input that has modify should edit value after input", async () => {
// Функция для модифицирования номера автобуса, функция удаляет всё кроме букв и вставляет -, после первых двух.
// AABBCC -> AA-BBCC
const modify = (x: unknown) => {
if (typeof x !== 'string') return '';
let str = x.replaceAll(/[^A-Z]/g, '');
str = str.slice(0, 6);
if (str.length < 6) return str;
return `${str.slice(0, 2)}-${str.slice(2, 6)}`
}
const component = defineComponent({
template: `<div><input-field name = "bus-number" :modify = "${modify}"/></div>`,
components: {
InputField
}
})
const app = mount(EmptyApp, {
slots: {
default: component
}
})
const form = app.vm.form as Form;
app.vm.loadingResource = true;
await app.vm.$nextTick();

const input = app.get("input");
await input.setValue("123")
expect(form.getValueByName('bus-number')).toBe("")

await input.setValue("A1A2B3BC55")
expect(form.getValueByName('bus-number')).toBe("AABBC")

await input.setValue("AABBCC")
expect(form.getValueByName('bus-number')).toBe("AA-BBCC")
})
test("Input that has pretty should prettify just view-value, not value in form", async () => {

const app = mount(AppInputTextPretty)
const form = app.vm.form as Form;

//app.vm.loadingResource = true;
await app.vm.$nextTick();

const input = app.get("input");
await input.setValue("jack")
await app.vm.$nextTick();
await wait()

expect(form.getValueByName('name')).toBe("jack")
expect(input.element.value).toBe('-jack-')


await input.setValue("a")
await wait()
expect(form.getValueByName('name')).toBe("a")
expect(input.element.value).toBe("-a-")

await input.setValue("")
await wait()
expect(form.getValueByName('name')).toBe("")
expect(input.element.value).toBe("")
})
test("Pretty and modify should be executed once", async () => {
const pretty = (x: any) => x;
const modify = (x: any) => x;

const mockPretty = jest.fn(pretty)
const mockModify = jest.fn(modify)

const component = defineComponent({
// @ts-ignore
template: `<div><input-field name = "username" :modify = "${(x) => mockModify(x)}" :pretty = "${(x) => mockPretty(x)}"/></div>`,
components: {
InputField
}
})
const app = mount(EmptyApp, {
slots: {
default: component
}
})
const form = app.vm.form as Form;
app.vm.loadingResource = true;
await app.vm.$nextTick()
const input = app.get('input');

await input.setValue("123");
await app.vm.$nextTick()
await wait()


expect(input.element.value).toBe("123")
expect(form.getValueByName('username')).toBe("123++++++++++")
expect(mockPretty.mock.calls.length).toBe(1)
expect(mockModify.mock.calls.length).toBe(1)


})
test("Max length attr should limit the length of value", () => {})
test("Placeholder in input should be shown if provided", () => {})
test("Prefix should be shown if provided", () => {})
test("Numeric attr should return only numeric value and reject entering chars", () => {
const form:any = null;

expect(form.getValueByName('age')).toBe(24);
})
})

0 comments on commit 221990e

Please sign in to comment.